Variables
- ID: identificación de vuelo.
- DATOP: Fecha de vuelo.
- FLTID: Número de vuelo
- DEPSTN: Punto de partida
- ARRSTN: Punto de llegada
- STD: Hora de salida programada
- STA: Hora prevista de llegada
- STATUS: Estado del vuelo
- AC: Código de aeronave
- target: Tiempo de retraso
Train y Test
- Train:
- En el conjunto de datos de train hay 107833 vuelos registrados.
- El rango de fechas de vuelos es desde el 01-01-2016 hasta 31-12-2018.
- Hay 1861 vuelos registrados. Cada vuelo puede estar registrado en diferentes fechas.
- En total son 132 puntos de destino diferentes y 128 puntos de llegada.
- Hay en total 5 estados (STATUS) de vuelo.
- Hay 68 códigos de aeronaves diferentes.
- El tiempo de retraso (target) está dado en minutos.
library(tidyverse)
# Train
dataTrain %>%
mutate(DATOP = as.Date(DATOP),
FLTID = as.factor(FLTID),
DEPSTN = as.factor(DEPSTN),
ARRSTN = as.factor(ARRSTN),
STD = as.POSIXct(STD),
STA = gsub("\\.", ":", STA),
STA = as.POSIXct(STA),
STATUS = as.factor(STATUS),
AC = as.factor(AC)) ->
dataTrain
head(dataTrain, n = 10L)
- Test:
- En el conjunto de test hay 9333 vuelos registrados.
- El rango de fechas de vuelos es desde 01-05-2016 hasta 20-09-2018.
- Hay 700 vuelos registrados.
- En total son 82 puntos de destino y 84 puntos de llegada diferentes.
- Los mismos 5 estados de vuelo.
- Hay 44 códigos de aeronaves.
# Test
dataTest %>%
mutate(DATOP = as.Date(DATOP),
FLTID = as.factor(FLTID),
DEPSTN = as.factor(DEPSTN),
ARRSTN = as.factor(ARRSTN),
STD = as.POSIXct(STD),
STA = gsub("\\.", ":", STA),
STA = as.POSIXct(STA),
STATUS = as.factor(STATUS),
AC = as.factor(AC)) ->
dataTest
head(dataTest, n = 10L)
Conteos
- ¿Cuántos vuelos (FLTID) del test coinciden con el train?:
FALSE TRUE
67 9266
- ¿Cuántos puntos de destino coinciden entre el test y train?:
FALSE TRUE
3 9330
- ¿Cuántos puntos de llegada coinciden entre el test y train?:
FALSE TRUE
3 9330
- ¿Cuántos códigos de aeronave coinciden entre el test y train?:
FALSE TRUE
21 9312
Nuevas variables
- Basado en las variables originales derivo las siguientes caracterÃsticas:
- DÃas del mes, dÃas de la semana, meses y año de vuelo.
- Agrego trimestres.
- Agrego semana del año.
- Agrego una variable que indique si es fin de semana.
- Agrego una nueva variable binaria que indique si es fin de mes. Si la fecha del vuelo está entre el 28 a 31 de cada mes, lo categorizo como fin de mes.
- Agrego una nueva variable binaria con fechas especiales. Aunque son muchas las fechas especiales que podrÃan ser tenidas en cuenta, incluyo el dÃa del trabajo (1 de mayo), el dÃa de san valentÃn (19 de septiembre), el dÃa internacional de la mujer (8 de marzo), año nuevo (01 de enero), dÃa de los reyes magos (06 de enero), noche de Hallowen (31 de octubre), noche buena (24 de diciembre), dÃa de navidad (25 de diciembre) y fin de año (31 de diciembre).
- Con el tiempo de salida obtengo la hora y establezco una nueva variable que define si el vuelo es en la madrugada, mañana, tarde o noche.
- Resto el tiempo de salida con el tiempo de llegada para conocer el tiempo promedio de vuelo. El resultado estará dado en minutos, sin embargo, lo convierto a horas dividiendo sobre 60. Hay vuelos con inconsistencias en los tiempos de partida y llegada, por tal motivo el tiempo de vuelo de aquellos que superan las 24 horas les agrego NA (363 registros en total).
- Con el tiempo promedio de vuelo clasifico los vuelos en vuelo corto (hasta 2 horas), vuelo moderado (entre 2 y 5 horas) y vuelo largo (mayor a 5 horas).
- Cuento el número de vuelos por punto de salida. Esto supongo que servirá para observar la demanda de cada sitio (ciudad o paÃs), esperando que donde haya más demanda posiblemente haya menor capacidad de reacción (¿o al contrario?) y quizás mayores retrasos. Lo mismo hago por punto de llegada.
- Cuento número de vuelos por código de aeronave, en el mismo orden de ideas de oferta-demanda.
- Opcionales: estas variables las agrego como opcionales porque podrÃan causar sobreajuste en los modelos.
- Para cada vuelo -FLTID promedio el tiempo de retraso. Los vuelos que están en el train que no pertenecen al test les agrego
NA. También se podrÃa calcular la mediana en lugar del promedio.
- Para cada vuelo obtengo la desviación estándar de la variable objetivo.
- Para cada vuelo obtengo el mÃnimo y máximo tiempo de retraso.
- Calculo el rango intercuartÃlico de retraso de cada vuelo.
Nuevas Train 1
# Nuevas variables train
dataTrain %>%
mutate(dayWeek = weekdays(DATOP),
mes = factor(month(DATOP)),
anio = factor(year(DATOP)),
trimestre = factor(quarters(DATOP)),
weekYear = week(DATOP),
endWeek = factor(if_else(dayWeek %in% c("sábado", "domingo"),
true = "Si", false = "No")),
horaVuelo = hour(STD),
horaVueloClas = if_else(
horaVuelo >= 0 & horaVuelo < 6,
true = "Magrudada",
false = if_else(
horaVuelo >= 6 & horaVuelo < 12,
true = "Mañana",
false = if_else(
horaVuelo >= 12 & horaVuelo < 18,
true = "Tarde",
false = "Noche"))),
tiempoVuelo = as.numeric(STA - STD)/60,
tiempoVuelo = ifelse(tiempoVuelo > 24, NA, tiempoVuelo),
tiempoClas = if_else(tiempoVuelo <= 2,
true = "Corto",
false = if_else(
tiempoVuelo > 5,
true = "Largo",
false = "Moderado"))) %>%
group_by(DEPSTN) %>%
mutate(vuelosPartida = n()) %>%
ungroup() %>%
group_by(ARRSTN) %>%
mutate(vuelosDestino = n()) %>%
ungroup() %>%
group_by(AC) %>%
mutate(vuelosAC = n()) %>%
group_by(FLTID) %>%
mutate(promedioRetraso = mean(target, na.rm = TRUE),
medianaRetraso = median(target, na.rm = TRUE),
desvRetraso = sd(target, na.rm = TRUE)) %>%
ungroup() %>%
mutate_if(is.character, as.factor) %>%
select(target, everything()) ->
newDataTrain
newDataTrain
Nuevas Test 1
# Nuevas variables test
dataTest %>%
mutate(dayWeek = weekdays(DATOP),
mes = factor(month(DATOP)),
anio = factor(year(DATOP)),
trimestre = factor(quarters(DATOP)),
weekYear = week(DATOP),
endWeek = factor(if_else(dayWeek %in% c("sábado", "domingo"),
true = "Si", false = "No")),
horaVuelo = hour(STD),
horaVueloClas = if_else(
horaVuelo >= 0 & horaVuelo < 6,
true = "Magrudada",
false = if_else(
horaVuelo >= 6 & horaVuelo < 12,
true = "Mañana",
false = if_else(
horaVuelo >= 12 & horaVuelo < 18,
true = "Tarde",
false = "Noche"))),
tiempoVuelo = as.numeric(STA - STD)/60,
tiempoVuelo = ifelse(tiempoVuelo > 24, NA, tiempoVuelo),
tiempoClas = if_else(tiempoVuelo <= 2,
true = "Corto",
false = if_else(
tiempoVuelo > 5,
true = "Largo",
false = "Moderado"))) %>%
group_by(DEPSTN) %>%
mutate(vuelosPartida = n()) %>%
ungroup() %>%
group_by(ARRSTN) %>%
mutate(vuelosDestino = n()) %>%
ungroup() %>%
group_by(AC) %>%
mutate(vuelosAC = n()) %>%
ungroup() %>%
mutate_if(is.character, as.factor) ->
newDataTest
# Juntando datos de tiempos de espera (target)
left_join(newDataTest,
newDataTrain %>%
select(FLTID, promedioRetraso:desvRetraso),
by = "FLTID") %>%
distinct(ID, .keep_all = TRUE) ->
newDataTest
newDataTest
Nuevas Train 2
# Fechas especiales
fechas <- c("5_1", "9_19", "3_8", "1_1", "1_6", "10_31", "12_24", "12_25",
"12_31")
# Nuevas variables train
dataTrain %>%
mutate(dayWeek = weekdays(DATOP),
mes = factor(month(DATOP)),
dia = mday(DATOP),
anio = factor(year(DATOP)),
trimestre = factor(quarters(DATOP)),
weekYear = week(DATOP),
endWeek = factor(if_else(dayWeek %in% c("sábado", "domingo"),
true = "Si", false = "No")),
finMes = if_else(dia %in% c(28, 29, 30, 31),
true = "Si", false = "No"),
horaVuelo = hour(STD),
horaVueloClas = if_else(
horaVuelo >= 0 & horaVuelo < 6,
true = "Magrudada",
false = if_else(
horaVuelo >= 6 & horaVuelo < 12,
true = "Mañana",
false = if_else(
horaVuelo >= 12 & horaVuelo < 18,
true = "Tarde",
false = "Noche"))),
tiempoVuelo = as.numeric(STA - STD)/60,
tiempoVuelo = ifelse(tiempoVuelo > 24, NA, tiempoVuelo),
tiempoClas = if_else(tiempoVuelo <= 2,
true = "Corto",
false = if_else(
tiempoVuelo > 5,
true = "Largo",
false = "Moderado"))) %>%
group_by(DEPSTN) %>%
mutate(vuelosPartida = n()) %>%
ungroup() %>%
group_by(ARRSTN) %>%
mutate(vuelosDestino = n()) %>%
ungroup() %>%
group_by(AC) %>%
mutate(vuelosAC = n()) %>%
group_by(FLTID) %>%
mutate(promedioRetraso = mean(target, na.rm = TRUE),
medianaRetraso = median(target, na.rm = TRUE),
desvRetraso = sd(target, na.rm = TRUE),
minRetraso = min(target, na.rm = TRUE),
maxRetraso = max(target, na.rm = TRUE),
RicRetraso = IQR(target, na.rm = TRUE)) %>%
ungroup() %>%
unite(mes, dia, col = "diaMes", remove = FALSE) %>%
mutate(fechaEspecial = if_else(diaMes %in% fechas,
true = "Si", false = "No")) %>%
mutate_if(is.character, as.factor) %>%
select(target, everything()) %>%
select(-diaMes) ->
newDataTrain
newDataTrain
Nuevas Test 2
# Nuevas variables test
dataTest %>%
mutate(dayWeek = weekdays(DATOP),
mes = factor(month(DATOP)),
dia = mday(DATOP),
anio = factor(year(DATOP)),
trimestre = factor(quarters(DATOP)),
weekYear = week(DATOP),
endWeek = factor(if_else(dayWeek %in% c("sábado", "domingo"),
true = "Si", false = "No")),
finMes = if_else(dia %in% c(28, 29, 30, 31),
true = "Si", false = "No"),
horaVuelo = hour(STD),
horaVueloClas = if_else(
horaVuelo >= 0 & horaVuelo < 6,
true = "Magrudada",
false = if_else(
horaVuelo >= 6 & horaVuelo < 12,
true = "Mañana",
false = if_else(
horaVuelo >= 12 & horaVuelo < 18,
true = "Tarde",
false = "Noche"))),
tiempoVuelo = as.numeric(STA - STD)/60,
tiempoVuelo = ifelse(tiempoVuelo > 24, NA, tiempoVuelo),
tiempoClas = if_else(tiempoVuelo <= 2,
true = "Corto",
false = if_else(
tiempoVuelo > 5,
true = "Largo",
false = "Moderado"))) %>%
unite(mes, dia, col = "diaMes", remove = FALSE) %>%
mutate(fechaEspecial = if_else(diaMes %in% fechas,
true = "Si", false = "No")) %>%
group_by(DEPSTN) %>%
mutate(vuelosPartida = n()) %>%
ungroup() %>%
group_by(ARRSTN) %>%
mutate(vuelosDestino = n()) %>%
ungroup() %>%
group_by(AC) %>%
mutate(vuelosAC = n()) %>%
ungroup() %>%
select(-diaMes) %>%
mutate_if(is.character, as.factor) ->
newDataTest
# Juntando datos de tiempos de espera (target)
left_join(newDataTest,
newDataTrain %>%
select(FLTID, promedioRetraso:RicRetraso),
by = "FLTID") %>%
distinct(ID, .keep_all = TRUE) ->
newDataTest
newDataTest
Modelos
Modelo 1
# Load data
load("../myData/Train1.Rdata")
load("../myData/Test1.Rdata")
dataSample <- data.table::fread("../data/sample.csv")
newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)
# Selection of variables for analysis
library(tidyverse)
dataTrain <- newDataTrain %>%
select(-c(ID, DATOP, STD, STA))
# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.70,
list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]
# Categorical features
catFeatures <- names(
dataTrain %>% select_if(is.factor)
)
# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
label = dfTrain[, 1],
categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
label = dfTest[, 1],
categorical_feature = catFeatures)
# Parameters for lightgbm
myParams <- list(
boosting = "gbdt",
objective = "regression",
metric = "rmse",
learning_rate = 0.1,
feature_fraction = 1,
bagging_fraction = 1,
max_depth = -1
)
# Train model
modelo <- lgb.train(params = myParams,
data = dataTrain_lgbm,
nrounds = 10000,
valids = list(test = dataTest_lgbm),
early_stopping_rounds = 500)
#best iter: 238
#best score: 107.9903
# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
select(-c(ID, DATOP, STD, STA))),
num_iteration = modelo$best_iter)
predicciones[predicciones < 0] <- 0
x11();hist(predicciones)
# Submission
dataSample %>%
select(ID) %>%
mutate(target = predicciones) ->
lgbmR1
# Export submission for zindi
write.csv(lgbmR1, file = "../submission/lgbmR1Vuelos.csv", row.names = FALSE)
Modelo 2
# Load data
load("../myData/Train1.Rdata")
load("../myData/Test1.Rdata")
dataSample <- data.table::fread("../data/sample.csv")
newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)
# Selection of variables for analysis
library(tidyverse)
dataTrain <- newDataTrain %>%
select(-c(ID, DATOP, STD, STA))
# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.70,
list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]
# Categorical features
catFeatures <- names(
dataTrain %>% select_if(is.factor)
)
# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
label = dfTrain[, 1],
categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
label = dfTest[, 1],
categorical_feature = catFeatures)
# Parameters for lightgbm
myParams <- list(
boosting = "gbdt",
objective = "regression",
metric = "rmse",
learning_rate = 0.1,
feature_fraction = 0.5,
bagging_fraction = 1,
min_data_in_leaf = 100,
num_leaves = 255,
max_depth = -1
)
# Train model
modelo <- lgb.train(params = myParams,
data = dataTrain_lgbm,
nrounds = 10000,
valids = list(test = dataTest_lgbm),
early_stopping_rounds = 500)
#best iter: 69
#best score: 109.0651
# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
select(-c(ID, DATOP, STD, STA))),
num_iteration = modelo$best_iter)
predicciones[predicciones < 0] <- 0
x11();hist(predicciones)
# Submission
dataSample %>%
select(ID) %>%
mutate(target = predicciones) ->
lgbmR2
# Export submission for zindi
write.csv(lgbmR2, file = "../submission/lgbmR2Vuelos.csv", row.names = FALSE)
Modelo 3
# Load data
load("../myData/Train1.Rdata")
load("../myData/Test1.Rdata")
dataSample <- data.table::fread("../data/sample.csv")
newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)
# Selection of variables for analysis
library(tidyverse)
dataTrain <- newDataTrain %>%
select(-c(ID, DATOP, STD, STA))
# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.80,
list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]
# Categorical features
catFeatures <- names(
dataTrain %>% select_if(is.factor)
)
# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
label = dfTrain[, 1],
categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
label = dfTest[, 1],
categorical_feature = catFeatures)
# Parameters for lightgbm
myParams <- list(
boosting = "gbdt",
objective = "regression",
metric = "rmse",
learning_rate = 0.01,
feature_fraction = 1,
bagging_fraction = 1,
max_depth = -1
)
# Train model
modelo <- lgb.train(params = myParams,
data = dataTrain_lgbm,
nrounds = 10000,
valids = list(test = dataTest_lgbm),
early_stopping_rounds = 500)
#best iter: 1426
#best score: 106.8384
# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
select(-c(ID, DATOP, STD, STA))),
num_iteration = modelo$best_iter)
predicciones[predicciones < 0] <- 0
x11();hist(predicciones)
# Submission
dataSample %>%
select(ID) %>%
mutate(target = predicciones) ->
lgbmR3
# Export submission for zindi
write.csv(lgbmR3, file = "../submission/lgbmR3Vuelos.csv", row.names = FALSE)
Modelo 4
# Load data
load("../myData/Train1.Rdata")
load("../myData/Test1.Rdata")
dataSample <- data.table::fread("../data/sample.csv")
newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)
# Selection of variables for analysis
library(tidyverse)
dataTrain <- newDataTrain %>%
select(-c(ID, DATOP, STD, STA))
# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.80,
list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]
# Categorical features
catFeatures <- names(
dataTrain %>% select_if(is.factor)
)
# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
label = dfTrain[, 1],
categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
label = dfTest[, 1],
categorical_feature = catFeatures)
# Parameters for lightgbm
myParams <- list(
boosting = "gbdt",
objective = "regression",
metric = "rmse",
learning_rate = 0.01,
feature_fraction = 0.5,
bagging_fraction = 1,
min_data_in_leaf = 100,
num_leaves = 255,
max_depth = -1
)
# Train model
modelo <- lgb.train(params = myParams,
data = dataTrain_lgbm,
nrounds = 10000,
valids = list(test = dataTest_lgbm),
early_stopping_rounds = 500)
#best iter: 878
#best score: 106.9247
# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
select(-c(ID, DATOP, STD, STA))),
num_iteration = modelo$best_iter)
predicciones[predicciones < 0] <- 0
x11();hist(predicciones)
# Submission
dataSample %>%
select(ID) %>%
mutate(target = predicciones) ->
lgbmR4
# Export submission for zindi
write.csv(lgbmR4, file = "../submission/lgbmR4Vuelos.csv", row.names = FALSE)
Modelo 5
# Load data
load("../myData/Train1.Rdata")
load("../myData/Test1.Rdata")
dataSample <- data.table::fread("../data/sample.csv")
newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)
# Selection of variables for analysis
library(tidyverse)
dataTrain <- newDataTrain %>%
select(-c(ID, DATOP, STD, STA))
# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.80,
list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]
# Categorical features
catFeatures <- names(
dataTrain %>% select_if(is.factor)
)
# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
label = dfTrain[, 1],
categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
label = dfTest[, 1],
categorical_feature = catFeatures)
# Parameters for lightgbm
myParams <- list(
boosting = "gbdt",
objective = "regression",
metric = "rmse",
learning_rate = 0.01,
feature_fraction = 1,
bagging_fraction = 1,
min_data_in_leaf = 300,
num_leaves = 500,
max_bin = 500,
max_depth = -1
)
# Train model
modelo <- lgb.train(params = myParams,
data = dataTrain_lgbm,
nrounds = 10000,
valids = list(test = dataTest_lgbm),
early_stopping_rounds = 500)
#best iter: 689
#best score: 108.1176
# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
select(-c(ID, DATOP, STD, STA))),
num_iteration = modelo$best_iter)
predicciones[predicciones < 0] <- 0
x11();hist(predicciones)
# Submission
dataSample %>%
select(ID) %>%
mutate(target = predicciones) ->
lgbmR5
# Export submission for zindi
write.csv(lgbmR5, file = "../submission/lgbmR5Vuelos.csv", row.names = FALSE)
Modelo 6
# Load data
load("../myData/Train2.Rdata")
load("../myData/Test2.Rdata")
dataSample <- data.table::fread("../data/sample.csv")
newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)
# Selection of variables for analysis
library(tidyverse)
dataTrain <- newDataTrain %>%
select(-c(ID, DATOP, STD, STA, minRetraso, maxRetraso))
# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.80,
list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]
# Categorical features
catFeatures <- names(
dataTrain %>% select_if(is.factor)
)
# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
label = dfTrain[, 1],
categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
label = dfTest[, 1],
categorical_feature = catFeatures)
# Parameters for lightgbm
myParams <- list(
boosting = "gbdt",
objective = "regression",
metric = "rmse",
learning_rate = 0.01,
feature_fraction = 1,
bagging_fraction = 1,
max_depth = -1
)
# Train model
modelo <- lgb.train(params = myParams,
data = dataTrain_lgbm,
nrounds = 10000,
valids = list(test = dataTest_lgbm),
early_stopping_rounds = 500)
#best iter: 1620
#best score: 105.6486
# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
select(-c(ID, DATOP, STD, STA,
minRetraso, maxRetraso))),
num_iteration = modelo$best_iter)
predicciones[predicciones < 0] <- 0
x11();hist(predicciones)
# Submission
dataSample %>%
select(ID) %>%
mutate(target = predicciones) ->
lgbmR6
# Export submission for zindi
write.csv(lgbmR6, file = "../submission/lgbmR6Vuelos.csv", row.names = FALSE)
Modelo 7
# Load data
load("../myData/Train2.Rdata")
load("../myData/Test2.Rdata")
dataSample <- data.table::fread("../data/sample.csv")
newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)
# Selection of variables for analysis
library(tidyverse)
dataTrain <- newDataTrain %>%
select(-c(ID, DATOP, STD, STA, dia, minRetraso, maxRetraso, RicRetraso, fechaEspecial))
# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.80,
list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]
# Categorical features
catFeatures <- names(
dataTrain %>% select_if(is.factor)
)
# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
label = dfTrain[, 1],
categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
label = dfTest[, 1],
categorical_feature = catFeatures)
# Parameters for lightgbm
myParams <- list(
boosting = "gbdt",
objective = "regression",
metric = "rmse",
learning_rate = 0.01,
feature_fraction = 1,
bagging_fraction = 1,
max_depth = -1
)
# Train model
modelo <- lgb.train(params = myParams,
data = dataTrain_lgbm,
nrounds = 10000,
valids = list(test = dataTest_lgbm),
early_stopping_rounds = 500)
#best iter: 1511
#best score: 106.6549
# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
select(-c(ID, DATOP, STD, STA,
dia, minRetraso, maxRetraso, RicRetraso,
fechaEspecial))),
num_iteration = modelo$best_iter)
predicciones[predicciones < 0] <- 0
x11();hist(predicciones)
# Submission
dataSample %>%
select(ID) %>%
mutate(target = predicciones) ->
lgbmR7
# Export submission for zindi
write.csv(lgbmR7, file = "../submission/lgbmR7Vuelos.csv", row.names = FALSE)
Modelo 8
# Load data
load("../myData/Train2.Rdata")
load("../myData/Test2.Rdata")
dataSample <- data.table::fread("../data/sample.csv")
newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)
# Selection of variables for analysis
library(tidyverse)
dataTrain <- newDataTrain %>%
select(-c(ID, DATOP, STD, STA, fechaEspecial))
# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.80,
list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]
# Categorical features
catFeatures <- names(
dataTrain %>% select_if(is.factor)
)
# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
label = dfTrain[, 1],
categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
label = dfTest[, 1],
categorical_feature = catFeatures)
# Parameters for lightgbm
myParams <- list(
boosting = "gbdt",
objective = "regression",
metric = "rmse",
learning_rate = 0.01,
feature_fraction = 1,
bagging_fraction = 1,
max_depth = -1
)
# Train model
modelo <- lgb.train(params = myParams,
data = dataTrain_lgbm,
nrounds = 10000,
valids = list(test = dataTest_lgbm),
early_stopping_rounds = 500)
#best iter: 1504
#best score: 105.7866
# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
select(-c(ID, DATOP, STD, STA, fechaEspecial))),
num_iteration = modelo$best_iter)
predicciones[predicciones < 0] <- 0
x11();hist(predicciones)
# Submission
dataSample %>%
select(ID) %>%
mutate(target = predicciones) ->
lgbmR8
# Export submission for zindi
write.csv(lgbmR8, file = "../submission/lgbmR8Vuelos.csv", row.names = FALSE)
Modelo 9
# Load data
load("../myData/Train2.Rdata")
load("../myData/Test2.Rdata")
dataSample <- data.table::fread("../data/sample.csv")
newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)
# Selection of variables for analysis
library(tidyverse)
dataTrain <- newDataTrain %>%
select(-c(ID, DATOP, STD, STA, fechaEspecial))
# caret for partition data
library(caret)
set.seed(123)
indx <- createDataPartition(y = dataTrain$target, times = 1, p = 0.90,
list = FALSE)
dfTrain <- dataTrain[indx, ]
dfTest <- dataTrain[-indx, ]
# Categorical features
catFeatures <- names(
dataTrain %>% select_if(is.factor)
)
# Data for lightgbm
library(lightgbm)
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dfTrain[, -1]),
label = dfTrain[, 1],
categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dfTest[, -1]),
label = dfTest[, 1],
categorical_feature = catFeatures)
# Parameters for lightgbm
myParams <- list(
boosting = "gbdt",
objective = "regression",
metric = "rmse",
learning_rate = 0.01,
feature_fraction = 1,
bagging_fraction = 1,
max_depth = -1
)
# Train model
modelo <- lgb.train(params = myParams,
data = dataTrain_lgbm,
nrounds = 30000,
valids = list(test = dataTest_lgbm),
early_stopping_rounds = 500)
#best iter: 19519
#best score: 102.1988
# Predictions
predicciones <- predict(modelo, data.matrix(newDataTest %>%
select(-c(ID, DATOP, STD, STA, fechaEspecial))),
num_iteration = modelo$best_iter)
predicciones[predicciones < 0] <- 0
x11();hist(predicciones)
# Submission
dataSample %>%
select(ID) %>%
mutate(target = predicciones) ->
lgbmR9
# Export submission for zindi
write.csv(lgbmR9, file = "../submission/lgbmR9Vuelos.csv",
row.names = FALSE)
Modelo 10
# Mod1 + KFold
# Load data
load("../myData/Train1.Rdata")
load("../myData/Test1.Rdata")
dataSample <- data.table::fread("../data/sample.csv")
newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)
# Selection of variables for analysis
library(tidyverse)
dataTrain <- newDataTrain %>%
select(-c(ID, DATOP, STD, STA))
# Categorical features
catFeatures <- names(
dataTrain %>% select_if(is.factor)
)
# caret for partition data with resample
library(caret)
set.seed(123)
index <- createDataPartition(y = dataTrain$target, times = 10, p = 0.70,
list = TRUE)
# Parameters for lightgbm
myParams <- list(
boosting = "gbdt",
objective = "regression",
metric = "rmse",
learning_rate = 0.1,
feature_fraction = 1,
bagging_fraction = 1,
max_depth = -1
)
# lightgbm with K-Fold manually (k = 10)
library(lightgbm)
k <- 10
predTest <- list()
bestScore <- c()
for (i in 1:k) {
# Data train and test
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dataTrain[index[[i]], -1]),
label = dataTrain[index[[i]], 1],
categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dataTrain[-index[[i]], -1]),
label = dataTrain[-index[[i]], 1],
categorical_feature = catFeatures)
# Train model
model <- lgb.train(params = myParams,
data = dataTrain_lgbm,
nrounds = 10000,
valids = list(test = dataTest_lgbm),
early_stopping_rounds = 500)
# Predictions
predictions <- predict(model,
data.matrix(newDataTest %>%
select(-c(ID, DATOP, STD, STA))),
num_iteration = model$best_iter)
predTest[[i]] = predictions
# Best score for iteration
bestScore[i] = model$best_score
# Next iteration
cat("Iteration:==========", i, "RSME:==========", model$best_score, "Ready!")
}
# Mean predictions
dataPred <- as.data.frame(predTest)
names(dataPred) <- paste0("Mod", 1:10)
predicciones <- apply(dataPred, 1, mean)
predicciones[predicciones < 0] <- 0
x11();hist(predicciones)
# Submission
dataSample %>%
select(ID) %>%
mutate(target = predicciones) ->
lgbmR10
# Export submission for zindi
write.csv(lgbmR10, file = "../submission/lgbmR10Vuelos.csv", row.names = FALSE)
Modelo 11
# Mod2 + KFold
# Load data
load("../myData/Train1.Rdata")
load("../myData/Test1.Rdata")
dataSample <- data.table::fread("../data/sample.csv")
newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)
# Selection of variables for analysis
library(tidyverse)
dataTrain <- newDataTrain %>%
select(-c(ID, DATOP, STD, STA))
# Categorical features
catFeatures <- names(
dataTrain %>% select_if(is.factor)
)
# caret for partition data with resample
library(caret)
set.seed(123)
index <- createDataPartition(y = dataTrain$target, times = 10, p = 0.70,
list = TRUE)
# Parameters for lightgbm
myParams <- list(
boosting = "gbdt",
objective = "regression",
metric = "rmse",
learning_rate = 0.1,
feature_fraction = 0.5,
bagging_fraction = 1,
min_data_in_leaf = 100,
num_leaves = 255,
max_depth = -1
)
# lightgbm with K-Fold manually (k = 10)
library(lightgbm)
k <- 10
predTest <- list()
bestScore <- c()
for (i in 1:k) {
# Data train and test
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dataTrain[index[[i]], -1]),
label = dataTrain[index[[i]], 1],
categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dataTrain[-index[[i]], -1]),
label = dataTrain[-index[[i]], 1],
categorical_feature = catFeatures)
# Train model
model <- lgb.train(params = myParams,
data = dataTrain_lgbm,
nrounds = 10000,
valids = list(test = dataTest_lgbm),
early_stopping_rounds = 500)
# Predictions
predictions <- predict(model,
data.matrix(newDataTest %>%
select(-c(ID, DATOP, STD, STA))),
num_iteration = model$best_iter)
predTest[[i]] = predictions
# Best score for iteration
bestScore[i] = model$best_score
# Next iteration
cat("Iteration:==========", i, "RSME:==========", model$best_score, "Ready!")
}
# Mean predictions
dataPred <- as.data.frame(predTest)
names(dataPred) <- paste0("Mod", 1:10)
predicciones <- apply(dataPred, 1, mean)
predicciones[predicciones < 0] <- 0
x11();hist(predicciones)
# Submission
dataSample %>%
select(ID) %>%
mutate(target = predicciones) ->
lgbmR11
# Export submission for zindi
write.csv(lgbmR11, file = "../submission/lgbmR11Vuelos.csv", row.names = FALSE)
Modelo 12
# Mod1 + KFold
# Load data
load("../myData/Train2.Rdata")
load("../myData/Test2.Rdata")
dataSample <- data.table::fread("../data/sample.csv")
newDataTrain <- as.data.frame(newDataTrain)
newDataTest <- as.data.frame(newDataTest)
# Selection of variables for analysis
library(tidyverse)
dataTrain <- newDataTrain %>%
select(-c(ID, DATOP, STD, STA))
# Categorical features
catFeatures <- names(
dataTrain %>% select_if(is.factor)
)
# caret for partition data with resample
library(caret)
set.seed(123)
index <- createDataPartition(y = dataTrain$target, times = 10, p = 0.70,
list = TRUE)
# Parameters for lightgbm
myParams <- list(
boosting = "gbdt",
objective = "regression",
metric = "rmse",
learning_rate = 0.01,
feature_fraction = 1,
bagging_fraction = 1,
max_depth = -1
)
# lightgbm with K-Fold manually (k = 10)
library(lightgbm)
k <- 10
predTest <- list()
bestScore <- c()
for (i in 1:k) {
# Data train and test
dataTrain_lgbm <- lgb.Dataset(data = data.matrix(dataTrain[index[[i]], -1]),
label = dataTrain[index[[i]], 1],
categorical_feature = catFeatures)
dataTest_lgbm <- lgb.Dataset(data = data.matrix(dataTrain[-index[[i]], -1]),
label = dataTrain[-index[[i]], 1],
categorical_feature = catFeatures)
# Train model
model <- lgb.train(params = myParams,
data = dataTrain_lgbm,
nrounds = 10000,
valids = list(test = dataTest_lgbm),
early_stopping_rounds = 500)
# Predictions
predictions <- predict(model,
data.matrix(newDataTest %>%
select(-c(ID, DATOP, STD, STA))),
num_iteration = model$best_iter)
predTest[[i]] = predictions
# Best score for iteration
bestScore[i] = model$best_score
# Next iteration
cat("Iteration:==========", i, "RSME:==========", model$best_score, "Ready!")
}
# Mean predictions
dataPred <- as.data.frame(predTest)
names(dataPred) <- paste0("Mod", 1:10)
predicciones <- apply(dataPred, 1, mean)
predicciones[predicciones < 0] <- 0
x11();hist(predicciones)
# Submission
dataSample %>%
select(ID) %>%
mutate(target = predicciones) ->
lgbmR12
# Export submission for zindi
write.csv(lgbmR12, file = "../submission/lgbmR12Vuelos.csv", row.names = FALSE)
LS0tDQp0aXRsZTogIlJldHJhc29zIGRlIHZ1ZWxvcyINCnN1YnRpdGxlOiAiQW7DoWxpc2lzIEV4cGxvcmF0b3JpbyINCmF1dGhvcjogIkVkaW1lciBEYXZpZCBKYXJhbWlsbG8gKCpzaWRlcmV1cyopIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGNzczogZXN0aWxvLmNzcw0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDoNCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlDQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgaGlnaGxpZ2h0OiBicmVlemVkYXJrDQogICAgDQotLS0NCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgZXZhbCA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduID0gImNlbnRlciIsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gOSwNCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFKQ0KYGBgDQoNCjxjZW50ZXI+DQo8aW1nIHNyYyA9ICJpbWcvaW1nMS5wbmciIC8+DQo8L2NlbnRlcj4NCg0KIyBSZXRvDQoNCi0gW1JldG8gZW4gRGF0YVNvdXJjZS5haV0oaHR0cHM6Ly93d3cuZGF0YXNvdXJjZS5haS9lcy9ob21lL2NvbXBldGl0aW9ucy9wcmVkaWNjaW9uLWRlLXJldHJhc29zLWRlLXZ1ZWxvcy1wYXJhLXVuYS1hZXJvbGluZWEpDQoNCiMgRGF0b3MNCg0KYGBge3J9DQpsaWJyYXJ5KGRhdGEudGFibGUpDQpkYXRhVHJhaW4gPC0gZnJlYWQoIi4uL2RhdGEvdHJhaW4uY3N2IikNCmRhdGFUZXN0IDwtIGZyZWFkKCIuLi9kYXRhL3Rlc3QuY3N2IikNCmRhdGFTYW1wbGUgPC0gZnJlYWQoIi4uL2RhdGEvc2FtcGxlLmNzdiIpDQpgYGANCg0KIyBWYXJpYWJsZXMNCg0KLSAqKklEOioqIGlkZW50aWZpY2FjacOzbiBkZSB2dWVsby4NCi0gKipEQVRPUDoqKiBGZWNoYSBkZSB2dWVsby4NCi0gKipGTFRJRDoqKiBOw7ptZXJvIGRlIHZ1ZWxvDQotICoqREVQU1ROOioqIFB1bnRvIGRlIHBhcnRpZGENCi0gKipBUlJTVE46KiogUHVudG8gZGUgbGxlZ2FkYQ0KLSAqKlNURDoqKiBIb3JhIGRlIHNhbGlkYSBwcm9ncmFtYWRhDQotICoqU1RBOioqIEhvcmEgcHJldmlzdGEgZGUgbGxlZ2FkYQ0KLSAqKlNUQVRVUzoqKiBFc3RhZG8gZGVsIHZ1ZWxvDQotICoqQUM6KiogQ8OzZGlnbyBkZSBhZXJvbmF2ZQ0KLSAqKnRhcmdldDoqKiBUaWVtcG8gZGUgcmV0cmFzbw0KDQojIFRyYWluIHkgVGVzdA0KDQotICoqVHJhaW46KioNCiAgLSBFbiBlbCBjb25qdW50byBkZSBkYXRvcyBkZSAqdHJhaW4qIGhheSAxMDc4MzMgdnVlbG9zIHJlZ2lzdHJhZG9zLg0KICAtIEVsIHJhbmdvIGRlIGZlY2hhcyBkZSB2dWVsb3MgZXMgZGVzZGUgZWwgMDEtMDEtMjAxNiBoYXN0YSAzMS0xMi0yMDE4Lg0KICAtIEhheSAxODYxIHZ1ZWxvcyByZWdpc3RyYWRvcy4gQ2FkYSB2dWVsbyBwdWVkZSBlc3RhciByZWdpc3RyYWRvIGVuIGRpZmVyZW50ZXMgZmVjaGFzLg0KICAtIEVuIHRvdGFsIHNvbiAxMzIgcHVudG9zIGRlIGRlc3Rpbm8gZGlmZXJlbnRlcyB5IDEyOCBwdW50b3MgZGUgbGxlZ2FkYS4NCiAgLSBIYXkgZW4gdG90YWwgNSBlc3RhZG9zICgqU1RBVFVTKikgZGUgdnVlbG8uDQogIC0gSGF5IDY4IGPDs2RpZ29zIGRlIGFlcm9uYXZlcyBkaWZlcmVudGVzLg0KICAtIEVsIHRpZW1wbyBkZSByZXRyYXNvICgqdGFyZ2V0KikgZXN0w6EgZGFkbyBlbiBtaW51dG9zLg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KIyBUcmFpbg0KZGF0YVRyYWluICU+JSANCiAgbXV0YXRlKERBVE9QID0gYXMuRGF0ZShEQVRPUCksDQogICAgICAgICBGTFRJRCA9IGFzLmZhY3RvcihGTFRJRCksDQogICAgICAgICBERVBTVE4gPSBhcy5mYWN0b3IoREVQU1ROKSwNCiAgICAgICAgIEFSUlNUTiA9IGFzLmZhY3RvcihBUlJTVE4pLA0KICAgICAgICAgU1REID0gYXMuUE9TSVhjdChTVEQpLA0KICAgICAgICAgU1RBID0gZ3N1YigiXFwuIiwgIjoiLCBTVEEpLA0KICAgICAgICAgU1RBID0gYXMuUE9TSVhjdChTVEEpLA0KICAgICAgICAgU1RBVFVTID0gYXMuZmFjdG9yKFNUQVRVUyksDQogICAgICAgICBBQyA9IGFzLmZhY3RvcihBQykpIC0+DQogIGRhdGFUcmFpbg0KaGVhZChkYXRhVHJhaW4sIG4gPSAxMEwpDQpgYGANCg0KLSAqKlRlc3Q6KioNCiAgLSBFbiBlbCBjb25qdW50byBkZSAqdGVzdCogaGF5IDkzMzMgdnVlbG9zIHJlZ2lzdHJhZG9zLg0KICAtIEVsIHJhbmdvIGRlIGZlY2hhcyBkZSB2dWVsb3MgZXMgZGVzZGUgMDEtMDUtMjAxNiBoYXN0YSAyMC0wOS0yMDE4Lg0KICAtIEhheSA3MDAgdnVlbG9zIHJlZ2lzdHJhZG9zLg0KICAtIEVuIHRvdGFsIHNvbiA4MiBwdW50b3MgZGUgZGVzdGlubyB5IDg0IHB1bnRvcyBkZSBsbGVnYWRhIGRpZmVyZW50ZXMuDQogIC0gTG9zIG1pc21vcyA1IGVzdGFkb3MgZGUgdnVlbG8uDQogIC0gSGF5IDQ0IGPDs2RpZ29zIGRlIGFlcm9uYXZlcy4NCg0KYGBge3J9DQojIFRlc3QNCmRhdGFUZXN0ICU+JSANCiAgbXV0YXRlKERBVE9QID0gYXMuRGF0ZShEQVRPUCksDQogICAgICAgICBGTFRJRCA9IGFzLmZhY3RvcihGTFRJRCksDQogICAgICAgICBERVBTVE4gPSBhcy5mYWN0b3IoREVQU1ROKSwNCiAgICAgICAgIEFSUlNUTiA9IGFzLmZhY3RvcihBUlJTVE4pLA0KICAgICAgICAgU1REID0gYXMuUE9TSVhjdChTVEQpLA0KICAgICAgICAgU1RBID0gZ3N1YigiXFwuIiwgIjoiLCBTVEEpLA0KICAgICAgICAgU1RBID0gYXMuUE9TSVhjdChTVEEpLA0KICAgICAgICAgU1RBVFVTID0gYXMuZmFjdG9yKFNUQVRVUyksDQogICAgICAgICBBQyA9IGFzLmZhY3RvcihBQykpIC0+DQogIGRhdGFUZXN0DQpoZWFkKGRhdGFUZXN0LCBuID0gMTBMKQ0KYGBgDQoNCiMgQ29udGVvcw0KDQotICoqwr9DdcOhbnRvcyB2dWVsb3MgKCpGTFRJRCopIGRlbCB0ZXN0IGNvaW5jaWRlbiBjb24gZWwgdHJhaW4/OioqDQoNCmBgYHtyfQ0KdGFibGUoZGF0YVRlc3QkRkxUSUQgJWluJSBkYXRhVHJhaW4kRkxUSUQpDQpgYGANCg0KLSAqKsK/Q3XDoW50b3MgcHVudG9zIGRlIGRlc3Rpbm8gY29pbmNpZGVuIGVudHJlIGVsIHRlc3QgeSB0cmFpbj86KioNCg0KYGBge3J9DQp0YWJsZShkYXRhVGVzdCRERVBTVE4gJWluJSBkYXRhVHJhaW4kREVQU1ROKQ0KYGBgDQoNCi0gKirCv0N1w6FudG9zIHB1bnRvcyBkZSBsbGVnYWRhIGNvaW5jaWRlbiBlbnRyZSBlbCB0ZXN0IHkgdHJhaW4/OioqDQoNCmBgYHtyfQ0KdGFibGUoZGF0YVRlc3QkQVJSU1ROICVpbiUgZGF0YVRyYWluJEFSUlNUTikNCmBgYA0KDQotICoqwr9DdcOhbnRvcyBjw7NkaWdvcyBkZSBhZXJvbmF2ZSBjb2luY2lkZW4gZW50cmUgZWwgdGVzdCB5IHRyYWluPzoqKg0KDQpgYGB7cn0NCnRhYmxlKGRhdGFUZXN0JEFDICVpbiUgZGF0YVRyYWluJEFDKQ0KYGBgDQoNCiMgTnVldmFzIHZhcmlhYmxlcw0KDQotIEJhc2FkbyBlbiBsYXMgdmFyaWFibGVzIG9yaWdpbmFsZXMgZGVyaXZvIGxhcyBzaWd1aWVudGVzIGNhcmFjdGVyw61zdGljYXM6DQogIC0gRMOtYXMgZGVsIG1lcywgZMOtYXMgZGUgbGEgc2VtYW5hLCBtZXNlcyB5IGHDsW8gZGUgdnVlbG8uDQogIC0gQWdyZWdvIHRyaW1lc3RyZXMuDQogIC0gQWdyZWdvIHNlbWFuYSBkZWwgYcOxby4NCiAgLSBBZ3JlZ28gdW5hIHZhcmlhYmxlIHF1ZSBpbmRpcXVlIHNpIGVzIGZpbiBkZSBzZW1hbmEuDQogIC0gQWdyZWdvIHVuYSBudWV2YSB2YXJpYWJsZSBiaW5hcmlhIHF1ZSBpbmRpcXVlIHNpIGVzIGZpbiBkZSBtZXMuIFNpIGxhIGZlY2hhIGRlbCB2dWVsbyBlc3TDoSBlbnRyZSBlbCAyOCBhIDMxIGRlIGNhZGEgbWVzLCBsbyBjYXRlZ29yaXpvIGNvbW8gZmluIGRlIG1lcy4NCiAgLSBBZ3JlZ28gdW5hIG51ZXZhIHZhcmlhYmxlIGJpbmFyaWEgY29uIGZlY2hhcyBlc3BlY2lhbGVzLiBBdW5xdWUgc29uIG11Y2hhcyBsYXMgZmVjaGFzIGVzcGVjaWFsZXMgcXVlIHBvZHLDrWFuIHNlciB0ZW5pZGFzIGVuIGN1ZW50YSwgaW5jbHV5byBlbCBkw61hIGRlbCB0cmFiYWpvICgxIGRlIG1heW8pLCBlbCBkw61hIGRlIHNhbiB2YWxlbnTDrW4gKDE5IGRlIHNlcHRpZW1icmUpLCBlbCBkw61hIGludGVybmFjaW9uYWwgZGUgbGEgbXVqZXIgKDggZGUgbWFyem8pLCBhw7FvIG51ZXZvICgwMSBkZSBlbmVybyksIGTDrWEgZGUgbG9zIHJleWVzIG1hZ29zICgwNiBkZSBlbmVybyksIG5vY2hlIGRlIEhhbGxvd2VuICgzMSBkZSBvY3R1YnJlKSwgbm9jaGUgYnVlbmEgKDI0IGRlIGRpY2llbWJyZSksIGTDrWEgZGUgbmF2aWRhZCAoMjUgZGUgZGljaWVtYnJlKSB5IGZpbiBkZSBhw7FvICgzMSBkZSBkaWNpZW1icmUpLg0KICAtIENvbiBlbCB0aWVtcG8gZGUgc2FsaWRhIG9idGVuZ28gbGEgKipob3JhKiogeSBlc3RhYmxlemNvIHVuYSBudWV2YSB2YXJpYWJsZSBxdWUgZGVmaW5lIHNpIGVsIHZ1ZWxvIGVzIGVuIGxhICptYWRydWdhZGEqLCAqbWHDsWFuYSosICp0YXJkZSogbyAqbm9jaGUqLg0KICAtIFJlc3RvIGVsIHRpZW1wbyBkZSBzYWxpZGEgY29uIGVsIHRpZW1wbyBkZSBsbGVnYWRhIHBhcmEgY29ub2NlciBlbCB0aWVtcG8gcHJvbWVkaW8gZGUgdnVlbG8uIEVsIHJlc3VsdGFkbyBlc3RhcsOhIGRhZG8gZW4gbWludXRvcywgc2luIGVtYmFyZ28sIGxvIGNvbnZpZXJ0byBhIGhvcmFzIGRpdmlkaWVuZG8gc29icmUgNjAuIDx0cmVkPkhheSB2dWVsb3MgY29uIGluY29uc2lzdGVuY2lhcyBlbiBsb3MgdGllbXBvcyBkZSBwYXJ0aWRhIHkgbGxlZ2FkYSwgcG9yIHRhbCBtb3Rpdm8gZWwgdGllbXBvIGRlIHZ1ZWxvIGRlIGFxdWVsbG9zIHF1ZSBzdXBlcmFuIGxhcyAyNCBob3JhcyBsZXMgYWdyZWdvIE5BICgzNjMgcmVnaXN0cm9zIGVuIHRvdGFsKS48L3RyZWQ+DQogIC0gQ29uIGVsIHRpZW1wbyBwcm9tZWRpbyBkZSB2dWVsbyBjbGFzaWZpY28gbG9zIHZ1ZWxvcyBlbiAqdnVlbG8gY29ydG8qIChoYXN0YSAyIGhvcmFzKSwgKnZ1ZWxvIG1vZGVyYWRvKiAoZW50cmUgMiB5IDUgaG9yYXMpIHkgKnZ1ZWxvIGxhcmdvKiAobWF5b3IgYSA1IGhvcmFzKS4NCiAgLSBDdWVudG8gZWwgbsO6bWVybyBkZSB2dWVsb3MgcG9yIHB1bnRvIGRlIHNhbGlkYS4gRXN0byBzdXBvbmdvIHF1ZSBzZXJ2aXLDoSBwYXJhIG9ic2VydmFyIGxhIGRlbWFuZGEgZGUgY2FkYSBzaXRpbyAoY2l1ZGFkIG8gcGHDrXMpLCBlc3BlcmFuZG8gcXVlIGRvbmRlIGhheWEgbcOhcyBkZW1hbmRhIHBvc2libGVtZW50ZSBoYXlhIG1lbm9yIGNhcGFjaWRhZCBkZSByZWFjY2nDs24gKMK/byBhbCBjb250cmFyaW8/KSB5IHF1aXrDoXMgbWF5b3JlcyByZXRyYXNvcy4gTG8gbWlzbW8gaGFnbyBwb3IgcHVudG8gZGUgbGxlZ2FkYS4NCiAgLSBDdWVudG8gbsO6bWVybyBkZSB2dWVsb3MgcG9yIGPDs2RpZ28gZGUgYWVyb25hdmUsIGVuIGVsIG1pc21vIG9yZGVuIGRlIGlkZWFzIGRlICpvZmVydGEtZGVtYW5kYSouDQogIC0gKipPcGNpb25hbGVzOioqIGVzdGFzIHZhcmlhYmxlcyBsYXMgYWdyZWdvIGNvbW8gb3BjaW9uYWxlcyBwb3JxdWUgcG9kcsOtYW4gY2F1c2FyIHNvYnJlYWp1c3RlIGVuIGxvcyBtb2RlbG9zLg0KICAgIC0gUGFyYSBjYWRhIHZ1ZWxvICotRkxUSUQqIHByb21lZGlvIGVsIHRpZW1wbyBkZSByZXRyYXNvLiBMb3MgdnVlbG9zIHF1ZSBlc3TDoW4gZW4gZWwgKnRyYWluKiBxdWUgbm8gcGVydGVuZWNlbiBhbCAqdGVzdCogbGVzIGFncmVnbyBgTkFgLiBUYW1iacOpbiBzZSBwb2Ryw61hIGNhbGN1bGFyIGxhIG1lZGlhbmEgZW4gbHVnYXIgZGVsIHByb21lZGlvLg0KICAgIC0gUGFyYSBjYWRhIHZ1ZWxvIG9idGVuZ28gbGEgZGVzdmlhY2nDs24gZXN0w6FuZGFyIGRlIGxhIHZhcmlhYmxlIG9iamV0aXZvLg0KICAgIC0gUGFyYSBjYWRhIHZ1ZWxvIG9idGVuZ28gZWwgbcOtbmltbyB5IG3DoXhpbW8gdGllbXBvIGRlIHJldHJhc28uDQogICAgLSBDYWxjdWxvIGVsIHJhbmdvIGludGVyY3VhcnTDrWxpY28gZGUgcmV0cmFzbyBkZSBjYWRhIHZ1ZWxvLg0KDQojIyBOdWV2YXMgVHJhaW4gMQ0KDQpgYGB7cn0NCiMgTnVldmFzIHZhcmlhYmxlcyB0cmFpbg0KZGF0YVRyYWluICU+JSANCiAgbXV0YXRlKGRheVdlZWsgPSB3ZWVrZGF5cyhEQVRPUCksDQogICAgICAgICBtZXMgPSBmYWN0b3IobW9udGgoREFUT1ApKSwNCiAgICAgICAgIGFuaW8gPSBmYWN0b3IoeWVhcihEQVRPUCkpLA0KICAgICAgICAgdHJpbWVzdHJlID0gZmFjdG9yKHF1YXJ0ZXJzKERBVE9QKSksDQogICAgICAgICB3ZWVrWWVhciA9IHdlZWsoREFUT1ApLA0KICAgICAgICAgZW5kV2VlayA9IGZhY3RvcihpZl9lbHNlKGRheVdlZWsgJWluJSBjKCJzw6FiYWRvIiwgImRvbWluZ28iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiU2kiLCBmYWxzZSA9ICJObyIpKSwNCiAgICAgICAgIGhvcmFWdWVsbyA9IGhvdXIoU1REKSwNCiAgICAgICAgIGhvcmFWdWVsb0NsYXMgPSBpZl9lbHNlKA0KICAgICAgICAgICBob3JhVnVlbG8gPj0gMCAmIGhvcmFWdWVsbyA8IDYsDQogICAgICAgICAgIHRydWUgPSAiTWFncnVkYWRhIiwNCiAgICAgICAgICAgZmFsc2UgPSBpZl9lbHNlKA0KICAgICAgICAgICAgIGhvcmFWdWVsbyA+PSA2ICYgaG9yYVZ1ZWxvIDwgMTIsDQogICAgICAgICAgICAgdHJ1ZSA9ICJNYcOxYW5hIiwNCiAgICAgICAgICAgICBmYWxzZSA9IGlmX2Vsc2UoDQogICAgICAgICAgICAgICBob3JhVnVlbG8gPj0gMTIgJiBob3JhVnVlbG8gPCAxOCwNCiAgICAgICAgICAgICAgIHRydWUgPSAiVGFyZGUiLA0KICAgICAgICAgICAgICAgZmFsc2UgPSAiTm9jaGUiKSkpLA0KICAgICAgICAgdGllbXBvVnVlbG8gPSBhcy5udW1lcmljKFNUQSAtIFNURCkvNjAsDQogICAgICAgICB0aWVtcG9WdWVsbyA9IGlmZWxzZSh0aWVtcG9WdWVsbyA+IDI0LCBOQSwgdGllbXBvVnVlbG8pLA0KICAgICAgICAgdGllbXBvQ2xhcyA9IGlmX2Vsc2UodGllbXBvVnVlbG8gPD0gMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiQ29ydG8iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFsc2UgPSBpZl9lbHNlKA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aWVtcG9WdWVsbyA+IDUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiTGFyZ28iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWxzZSA9ICJNb2RlcmFkbyIpKSkgJT4lIA0KICBncm91cF9ieShERVBTVE4pICU+JSANCiAgbXV0YXRlKHZ1ZWxvc1BhcnRpZGEgPSBuKCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZ3JvdXBfYnkoQVJSU1ROKSAlPiUgDQogIG11dGF0ZSh2dWVsb3NEZXN0aW5vID0gbigpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIGdyb3VwX2J5KEFDKSAlPiUgDQogIG11dGF0ZSh2dWVsb3NBQyA9IG4oKSkgJT4lIA0KICBncm91cF9ieShGTFRJRCkgJT4lIA0KICBtdXRhdGUocHJvbWVkaW9SZXRyYXNvID0gbWVhbih0YXJnZXQsIG5hLnJtID0gVFJVRSksDQogICAgICAgICBtZWRpYW5hUmV0cmFzbyA9IG1lZGlhbih0YXJnZXQsIG5hLnJtID0gVFJVRSksDQogICAgICAgICBkZXN2UmV0cmFzbyA9IHNkKHRhcmdldCwgbmEucm0gPSBUUlVFKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGVfaWYoaXMuY2hhcmFjdGVyLCBhcy5mYWN0b3IpICU+JSANCiAgc2VsZWN0KHRhcmdldCwgZXZlcnl0aGluZygpKSAtPg0KICBuZXdEYXRhVHJhaW4NCm5ld0RhdGFUcmFpbg0KYGBgDQoNCiMjIE51ZXZhcyBUZXN0IDENCg0KYGBge3J9DQojIE51ZXZhcyB2YXJpYWJsZXMgdGVzdA0KZGF0YVRlc3QgJT4lIA0KICBtdXRhdGUoZGF5V2VlayA9IHdlZWtkYXlzKERBVE9QKSwNCiAgICAgICAgIG1lcyA9IGZhY3Rvcihtb250aChEQVRPUCkpLA0KICAgICAgICAgYW5pbyA9IGZhY3Rvcih5ZWFyKERBVE9QKSksDQogICAgICAgICB0cmltZXN0cmUgPSBmYWN0b3IocXVhcnRlcnMoREFUT1ApKSwNCiAgICAgICAgIHdlZWtZZWFyID0gd2VlayhEQVRPUCksDQogICAgICAgICBlbmRXZWVrID0gZmFjdG9yKGlmX2Vsc2UoZGF5V2VlayAlaW4lIGMoInPDoWJhZG8iLCAiZG9taW5nbyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJTaSIsIGZhbHNlID0gIk5vIikpLA0KICAgICAgICAgaG9yYVZ1ZWxvID0gaG91cihTVEQpLA0KICAgICAgICAgaG9yYVZ1ZWxvQ2xhcyA9IGlmX2Vsc2UoDQogICAgICAgICAgIGhvcmFWdWVsbyA+PSAwICYgaG9yYVZ1ZWxvIDwgNiwNCiAgICAgICAgICAgdHJ1ZSA9ICJNYWdydWRhZGEiLA0KICAgICAgICAgICBmYWxzZSA9IGlmX2Vsc2UoDQogICAgICAgICAgICAgaG9yYVZ1ZWxvID49IDYgJiBob3JhVnVlbG8gPCAxMiwNCiAgICAgICAgICAgICB0cnVlID0gIk1hw7FhbmEiLA0KICAgICAgICAgICAgIGZhbHNlID0gaWZfZWxzZSgNCiAgICAgICAgICAgICAgIGhvcmFWdWVsbyA+PSAxMiAmIGhvcmFWdWVsbyA8IDE4LA0KICAgICAgICAgICAgICAgdHJ1ZSA9ICJUYXJkZSIsDQogICAgICAgICAgICAgICBmYWxzZSA9ICJOb2NoZSIpKSksDQogICAgICAgICB0aWVtcG9WdWVsbyA9IGFzLm51bWVyaWMoU1RBIC0gU1REKS82MCwNCiAgICAgICAgIHRpZW1wb1Z1ZWxvID0gaWZlbHNlKHRpZW1wb1Z1ZWxvID4gMjQsIE5BLCB0aWVtcG9WdWVsbyksDQogICAgICAgICB0aWVtcG9DbGFzID0gaWZfZWxzZSh0aWVtcG9WdWVsbyA8PSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJDb3J0byIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWxzZSA9IGlmX2Vsc2UoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpZW1wb1Z1ZWxvID4gNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJMYXJnbyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlID0gIk1vZGVyYWRvIikpKSAlPiUgDQogIGdyb3VwX2J5KERFUFNUTikgJT4lIA0KICBtdXRhdGUodnVlbG9zUGFydGlkYSA9IG4oKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBncm91cF9ieShBUlJTVE4pICU+JSANCiAgbXV0YXRlKHZ1ZWxvc0Rlc3Rpbm8gPSBuKCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZ3JvdXBfYnkoQUMpICU+JSANCiAgbXV0YXRlKHZ1ZWxvc0FDID0gbigpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLmZhY3RvcikgLT4NCiAgbmV3RGF0YVRlc3QNCiMgSnVudGFuZG8gZGF0b3MgZGUgdGllbXBvcyBkZSBlc3BlcmEgKHRhcmdldCkNCmxlZnRfam9pbihuZXdEYXRhVGVzdCwNCiAgICAgICAgICBuZXdEYXRhVHJhaW4gJT4lDQogICAgICAgICAgICBzZWxlY3QoRkxUSUQsIHByb21lZGlvUmV0cmFzbzpkZXN2UmV0cmFzbyksDQogICAgICAgICAgYnkgPSAiRkxUSUQiKSAlPiUNCiAgZGlzdGluY3QoSUQsIC5rZWVwX2FsbCA9IFRSVUUpIC0+DQogIG5ld0RhdGFUZXN0DQpuZXdEYXRhVGVzdA0KYGBgDQoNCiMjIEV4cG9ydGFuZG8gZGF0b3MgMQ0KDQpgYGB7cn0NCiMgTnVldmFzIDENCnNhdmUobmV3RGF0YVRyYWluLCBmaWxlID0gIi4uL215RGF0YS9UcmFpbjEuUmRhdGEiLCBjb21wcmVzcyA9ICJ4eiIpDQpzYXZlKG5ld0RhdGFUZXN0LCBmaWxlID0gIi4uL215RGF0YS9UZXN0MS5SZGF0YSIsIGNvbXByZXNzID0gInh6IikNCmBgYA0KDQojIyBOdWV2YXMgVHJhaW4gMg0KDQpgYGB7cn0NCiMgRmVjaGFzIGVzcGVjaWFsZXMNCmZlY2hhcyA8LSBjKCI1XzEiLCAiOV8xOSIsICIzXzgiLCAiMV8xIiwgIjFfNiIsICIxMF8zMSIsICIxMl8yNCIsICIxMl8yNSIsDQogICAgICAgICAgICAiMTJfMzEiKQ0KDQojIE51ZXZhcyB2YXJpYWJsZXMgdHJhaW4NCmRhdGFUcmFpbiAlPiUgDQogIG11dGF0ZShkYXlXZWVrID0gd2Vla2RheXMoREFUT1ApLA0KICAgICAgICAgbWVzID0gZmFjdG9yKG1vbnRoKERBVE9QKSksDQogICAgICAgICBkaWEgPSBtZGF5KERBVE9QKSwNCiAgICAgICAgIGFuaW8gPSBmYWN0b3IoeWVhcihEQVRPUCkpLA0KICAgICAgICAgdHJpbWVzdHJlID0gZmFjdG9yKHF1YXJ0ZXJzKERBVE9QKSksDQogICAgICAgICB3ZWVrWWVhciA9IHdlZWsoREFUT1ApLA0KICAgICAgICAgZW5kV2VlayA9IGZhY3RvcihpZl9lbHNlKGRheVdlZWsgJWluJSBjKCJzw6FiYWRvIiwgImRvbWluZ28iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiU2kiLCBmYWxzZSA9ICJObyIpKSwNCiAgICAgICAgIGZpbk1lcyA9IGlmX2Vsc2UoZGlhICVpbiUgYygyOCwgMjksIDMwLCAzMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiU2kiLCBmYWxzZSA9ICJObyIpLA0KICAgICAgICAgaG9yYVZ1ZWxvID0gaG91cihTVEQpLA0KICAgICAgICAgaG9yYVZ1ZWxvQ2xhcyA9IGlmX2Vsc2UoDQogICAgICAgICAgIGhvcmFWdWVsbyA+PSAwICYgaG9yYVZ1ZWxvIDwgNiwNCiAgICAgICAgICAgdHJ1ZSA9ICJNYWdydWRhZGEiLA0KICAgICAgICAgICBmYWxzZSA9IGlmX2Vsc2UoDQogICAgICAgICAgICAgaG9yYVZ1ZWxvID49IDYgJiBob3JhVnVlbG8gPCAxMiwNCiAgICAgICAgICAgICB0cnVlID0gIk1hw7FhbmEiLA0KICAgICAgICAgICAgIGZhbHNlID0gaWZfZWxzZSgNCiAgICAgICAgICAgICAgIGhvcmFWdWVsbyA+PSAxMiAmIGhvcmFWdWVsbyA8IDE4LA0KICAgICAgICAgICAgICAgdHJ1ZSA9ICJUYXJkZSIsDQogICAgICAgICAgICAgICBmYWxzZSA9ICJOb2NoZSIpKSksDQogICAgICAgICB0aWVtcG9WdWVsbyA9IGFzLm51bWVyaWMoU1RBIC0gU1REKS82MCwNCiAgICAgICAgIHRpZW1wb1Z1ZWxvID0gaWZlbHNlKHRpZW1wb1Z1ZWxvID4gMjQsIE5BLCB0aWVtcG9WdWVsbyksDQogICAgICAgICB0aWVtcG9DbGFzID0gaWZfZWxzZSh0aWVtcG9WdWVsbyA8PSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJDb3J0byIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWxzZSA9IGlmX2Vsc2UoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpZW1wb1Z1ZWxvID4gNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJMYXJnbyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlID0gIk1vZGVyYWRvIikpKSAlPiUgDQogIGdyb3VwX2J5KERFUFNUTikgJT4lIA0KICBtdXRhdGUodnVlbG9zUGFydGlkYSA9IG4oKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBncm91cF9ieShBUlJTVE4pICU+JSANCiAgbXV0YXRlKHZ1ZWxvc0Rlc3Rpbm8gPSBuKCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZ3JvdXBfYnkoQUMpICU+JSANCiAgbXV0YXRlKHZ1ZWxvc0FDID0gbigpKSAlPiUgDQogIGdyb3VwX2J5KEZMVElEKSAlPiUgDQogIG11dGF0ZShwcm9tZWRpb1JldHJhc28gPSBtZWFuKHRhcmdldCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgIG1lZGlhbmFSZXRyYXNvID0gbWVkaWFuKHRhcmdldCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgIGRlc3ZSZXRyYXNvID0gc2QodGFyZ2V0LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgbWluUmV0cmFzbyA9IG1pbih0YXJnZXQsIG5hLnJtID0gVFJVRSksDQogICAgICAgICBtYXhSZXRyYXNvID0gbWF4KHRhcmdldCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgIFJpY1JldHJhc28gPSBJUVIodGFyZ2V0LCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIHVuaXRlKG1lcywgZGlhLCBjb2wgPSAiZGlhTWVzIiwgcmVtb3ZlID0gRkFMU0UpICU+JSANCiAgbXV0YXRlKGZlY2hhRXNwZWNpYWwgPSBpZl9lbHNlKGRpYU1lcyAlaW4lIGZlY2hhcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiU2kiLCBmYWxzZSA9ICJObyIpKSAlPiUgDQogIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLmZhY3RvcikgJT4lIA0KICBzZWxlY3QodGFyZ2V0LCBldmVyeXRoaW5nKCkpICU+JSANCiAgc2VsZWN0KC1kaWFNZXMpIC0+DQogIG5ld0RhdGFUcmFpbg0KICANCm5ld0RhdGFUcmFpbg0KYGBgDQoNCg0KIyMgTnVldmFzIFRlc3QgMg0KDQpgYGB7cn0NCiMgTnVldmFzIHZhcmlhYmxlcyB0ZXN0DQpkYXRhVGVzdCAlPiUgDQogIG11dGF0ZShkYXlXZWVrID0gd2Vla2RheXMoREFUT1ApLA0KICAgICAgICAgbWVzID0gZmFjdG9yKG1vbnRoKERBVE9QKSksDQogICAgICAgICBkaWEgPSBtZGF5KERBVE9QKSwNCiAgICAgICAgIGFuaW8gPSBmYWN0b3IoeWVhcihEQVRPUCkpLA0KICAgICAgICAgdHJpbWVzdHJlID0gZmFjdG9yKHF1YXJ0ZXJzKERBVE9QKSksDQogICAgICAgICB3ZWVrWWVhciA9IHdlZWsoREFUT1ApLA0KICAgICAgICAgZW5kV2VlayA9IGZhY3RvcihpZl9lbHNlKGRheVdlZWsgJWluJSBjKCJzw6FiYWRvIiwgImRvbWluZ28iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiU2kiLCBmYWxzZSA9ICJObyIpKSwNCiAgICAgICAgIGZpbk1lcyA9IGlmX2Vsc2UoZGlhICVpbiUgYygyOCwgMjksIDMwLCAzMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiU2kiLCBmYWxzZSA9ICJObyIpLA0KICAgICAgICAgaG9yYVZ1ZWxvID0gaG91cihTVEQpLA0KICAgICAgICAgaG9yYVZ1ZWxvQ2xhcyA9IGlmX2Vsc2UoDQogICAgICAgICAgIGhvcmFWdWVsbyA+PSAwICYgaG9yYVZ1ZWxvIDwgNiwNCiAgICAgICAgICAgdHJ1ZSA9ICJNYWdydWRhZGEiLA0KICAgICAgICAgICBmYWxzZSA9IGlmX2Vsc2UoDQogICAgICAgICAgICAgaG9yYVZ1ZWxvID49IDYgJiBob3JhVnVlbG8gPCAxMiwNCiAgICAgICAgICAgICB0cnVlID0gIk1hw7FhbmEiLA0KICAgICAgICAgICAgIGZhbHNlID0gaWZfZWxzZSgNCiAgICAgICAgICAgICAgIGhvcmFWdWVsbyA+PSAxMiAmIGhvcmFWdWVsbyA8IDE4LA0KICAgICAgICAgICAgICAgdHJ1ZSA9ICJUYXJkZSIsDQogICAgICAgICAgICAgICBmYWxzZSA9ICJOb2NoZSIpKSksDQogICAgICAgICB0aWVtcG9WdWVsbyA9IGFzLm51bWVyaWMoU1RBIC0gU1REKS82MCwNCiAgICAgICAgIHRpZW1wb1Z1ZWxvID0gaWZlbHNlKHRpZW1wb1Z1ZWxvID4gMjQsIE5BLCB0aWVtcG9WdWVsbyksDQogICAgICAgICB0aWVtcG9DbGFzID0gaWZfZWxzZSh0aWVtcG9WdWVsbyA8PSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJDb3J0byIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWxzZSA9IGlmX2Vsc2UoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpZW1wb1Z1ZWxvID4gNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJMYXJnbyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlID0gIk1vZGVyYWRvIikpKSAlPiUgDQogIHVuaXRlKG1lcywgZGlhLCBjb2wgPSAiZGlhTWVzIiwgcmVtb3ZlID0gRkFMU0UpICU+JSANCiAgbXV0YXRlKGZlY2hhRXNwZWNpYWwgPSBpZl9lbHNlKGRpYU1lcyAlaW4lIGZlY2hhcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiU2kiLCBmYWxzZSA9ICJObyIpKSAlPiUgDQogIGdyb3VwX2J5KERFUFNUTikgJT4lIA0KICBtdXRhdGUodnVlbG9zUGFydGlkYSA9IG4oKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBncm91cF9ieShBUlJTVE4pICU+JSANCiAgbXV0YXRlKHZ1ZWxvc0Rlc3Rpbm8gPSBuKCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZ3JvdXBfYnkoQUMpICU+JSANCiAgbXV0YXRlKHZ1ZWxvc0FDID0gbigpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIHNlbGVjdCgtZGlhTWVzKSAlPiUgDQogIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLmZhY3RvcikgLT4NCiAgbmV3RGF0YVRlc3QNCg0KIyBKdW50YW5kbyBkYXRvcyBkZSB0aWVtcG9zIGRlIGVzcGVyYSAodGFyZ2V0KQ0KbGVmdF9qb2luKG5ld0RhdGFUZXN0LA0KICAgICAgICAgIG5ld0RhdGFUcmFpbiAlPiUNCiAgICAgICAgICAgIHNlbGVjdChGTFRJRCwgcHJvbWVkaW9SZXRyYXNvOlJpY1JldHJhc28pLA0KICAgICAgICAgIGJ5ID0gIkZMVElEIikgJT4lDQogIGRpc3RpbmN0KElELCAua2VlcF9hbGwgPSBUUlVFKSAtPg0KICBuZXdEYXRhVGVzdA0KDQpuZXdEYXRhVGVzdA0KYGBgDQoNCiMjIEV4cG9ydGFuZG8gZGF0b3MgMg0KDQpgYGB7cn0NCiMgTnVldmFzIDINCnNhdmUobmV3RGF0YVRyYWluLCBmaWxlID0gIi4uL215RGF0YS9UcmFpbjIuUmRhdGEiLCBjb21wcmVzcyA9ICJ4eiIpDQpzYXZlKG5ld0RhdGFUZXN0LCBmaWxlID0gIi4uL215RGF0YS9UZXN0Mi5SZGF0YSIsIGNvbXByZXNzID0gInh6IikNCmBgYA0KDQojIE1vZGVsb3MNCg0KIyMgTW9kZWxvIDENCg0KYGBge3J9DQojIExvYWQgZGF0YQ0KbG9hZCgiLi4vbXlEYXRhL1RyYWluMS5SZGF0YSIpDQpsb2FkKCIuLi9teURhdGEvVGVzdDEuUmRhdGEiKQ0KZGF0YVNhbXBsZSA8LSBkYXRhLnRhYmxlOjpmcmVhZCgiLi4vZGF0YS9zYW1wbGUuY3N2IikNCg0KbmV3RGF0YVRyYWluIDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRyYWluKQ0KbmV3RGF0YVRlc3QgPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVGVzdCkNCg0KIyBTZWxlY3Rpb24gb2YgdmFyaWFibGVzIGZvciBhbmFseXNpcyANCmxpYnJhcnkodGlkeXZlcnNlKQ0KZGF0YVRyYWluIDwtIG5ld0RhdGFUcmFpbiAlPiUNCiAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEpKQ0KDQojIGNhcmV0IGZvciBwYXJ0aXRpb24gZGF0YQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDEyMykNCmluZHggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5ID0gZGF0YVRyYWluJHRhcmdldCwgdGltZXMgPSAxLCBwID0gMC43MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UpDQpkZlRyYWluIDwtIGRhdGFUcmFpbltpbmR4LCBdDQpkZlRlc3QgPC0gZGF0YVRyYWluWy1pbmR4LCBdDQoNCiMgQ2F0ZWdvcmljYWwgZmVhdHVyZXMNCmNhdEZlYXR1cmVzIDwtIG5hbWVzKA0KICBkYXRhVHJhaW4gJT4lIHNlbGVjdF9pZihpcy5mYWN0b3IpDQopDQoNCiMgRGF0YSBmb3IgbGlnaHRnYm0NCmxpYnJhcnkobGlnaHRnYm0pDQpkYXRhVHJhaW5fbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGZUcmFpblssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRmVHJhaW5bLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCmRhdGFUZXN0X2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRmVGVzdFssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGZUZXN0WywgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCg0KIyBQYXJhbWV0ZXJzIGZvciBsaWdodGdibQ0KbXlQYXJhbXMgPC0gbGlzdCgNCiAgYm9vc3RpbmcgPSAiZ2JkdCIsDQogIG9iamVjdGl2ZSA9ICJyZWdyZXNzaW9uIiwNCiAgbWV0cmljID0gInJtc2UiLA0KICBsZWFybmluZ19yYXRlID0gMC4xLA0KICBmZWF0dXJlX2ZyYWN0aW9uID0gMSwNCiAgYmFnZ2luZ19mcmFjdGlvbiA9IDEsDQogIG1heF9kZXB0aCA9IC0xDQopDQoNCiMgVHJhaW4gbW9kZWwNCm1vZGVsbyA8LSBsZ2IudHJhaW4ocGFyYW1zID0gbXlQYXJhbXMsDQogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhVHJhaW5fbGdibSwNCiAgICAgICAgICAgICAgICAgICAgbnJvdW5kcyA9IDEwMDAwLA0KICAgICAgICAgICAgICAgICAgICB2YWxpZHMgPSBsaXN0KHRlc3QgPSBkYXRhVGVzdF9sZ2JtKSwNCiAgICAgICAgICAgICAgICAgICAgZWFybHlfc3RvcHBpbmdfcm91bmRzID0gNTAwKQ0KDQojYmVzdCBpdGVyOiAyMzgNCiNiZXN0IHNjb3JlOiAxMDcuOTkwMw0KDQojIFByZWRpY3Rpb25zDQpwcmVkaWNjaW9uZXMgPC0gcHJlZGljdChtb2RlbG8sIGRhdGEubWF0cml4KG5ld0RhdGFUZXN0ICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBKSkpLCANCiAgICAgICAgICAgICAgICAgICAgICAgIG51bV9pdGVyYXRpb24gPSBtb2RlbG8kYmVzdF9pdGVyKQ0KDQpwcmVkaWNjaW9uZXNbcHJlZGljY2lvbmVzIDwgMF0gPC0gMA0KeDExKCk7aGlzdChwcmVkaWNjaW9uZXMpDQoNCiMgU3VibWlzc2lvbg0KZGF0YVNhbXBsZSAlPiUgDQogIHNlbGVjdChJRCkgJT4lIA0KICBtdXRhdGUodGFyZ2V0ID0gcHJlZGljY2lvbmVzKSAtPg0KICBsZ2JtUjENCg0KIyBFeHBvcnQgc3VibWlzc2lvbiBmb3IgemluZGkNCndyaXRlLmNzdihsZ2JtUjEsIGZpbGUgPSAiLi4vc3VibWlzc2lvbi9sZ2JtUjFWdWVsb3MuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCg0KIyMgTW9kZWxvIDINCg0KYGBge3J9DQojIExvYWQgZGF0YQ0KbG9hZCgiLi4vbXlEYXRhL1RyYWluMS5SZGF0YSIpDQpsb2FkKCIuLi9teURhdGEvVGVzdDEuUmRhdGEiKQ0KZGF0YVNhbXBsZSA8LSBkYXRhLnRhYmxlOjpmcmVhZCgiLi4vZGF0YS9zYW1wbGUuY3N2IikNCg0KbmV3RGF0YVRyYWluIDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRyYWluKQ0KbmV3RGF0YVRlc3QgPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVGVzdCkNCg0KIyBTZWxlY3Rpb24gb2YgdmFyaWFibGVzIGZvciBhbmFseXNpcyANCmxpYnJhcnkodGlkeXZlcnNlKQ0KZGF0YVRyYWluIDwtIG5ld0RhdGFUcmFpbiAlPiUNCiAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEpKQ0KDQojIGNhcmV0IGZvciBwYXJ0aXRpb24gZGF0YQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDEyMykNCmluZHggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5ID0gZGF0YVRyYWluJHRhcmdldCwgdGltZXMgPSAxLCBwID0gMC43MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UpDQpkZlRyYWluIDwtIGRhdGFUcmFpbltpbmR4LCBdDQpkZlRlc3QgPC0gZGF0YVRyYWluWy1pbmR4LCBdDQoNCiMgQ2F0ZWdvcmljYWwgZmVhdHVyZXMNCmNhdEZlYXR1cmVzIDwtIG5hbWVzKA0KICBkYXRhVHJhaW4gJT4lIHNlbGVjdF9pZihpcy5mYWN0b3IpDQopDQoNCiMgRGF0YSBmb3IgbGlnaHRnYm0NCmxpYnJhcnkobGlnaHRnYm0pDQpkYXRhVHJhaW5fbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGZUcmFpblssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRmVHJhaW5bLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCmRhdGFUZXN0X2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRmVGVzdFssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGZUZXN0WywgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCg0KIyBQYXJhbWV0ZXJzIGZvciBsaWdodGdibQ0KbXlQYXJhbXMgPC0gbGlzdCgNCiAgYm9vc3RpbmcgPSAiZ2JkdCIsDQogIG9iamVjdGl2ZSA9ICJyZWdyZXNzaW9uIiwNCiAgbWV0cmljID0gInJtc2UiLA0KICBsZWFybmluZ19yYXRlID0gMC4xLA0KICBmZWF0dXJlX2ZyYWN0aW9uID0gMC41LA0KICBiYWdnaW5nX2ZyYWN0aW9uID0gMSwNCiAgbWluX2RhdGFfaW5fbGVhZiA9IDEwMCwNCiAgbnVtX2xlYXZlcyA9IDI1NSwNCiAgbWF4X2RlcHRoID0gLTENCikNCg0KIyBUcmFpbiBtb2RlbA0KbW9kZWxvIDwtIGxnYi50cmFpbihwYXJhbXMgPSBteVBhcmFtcywNCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFUcmFpbl9sZ2JtLA0KICAgICAgICAgICAgICAgICAgICBucm91bmRzID0gMTAwMDAsDQogICAgICAgICAgICAgICAgICAgIHZhbGlkcyA9IGxpc3QodGVzdCA9IGRhdGFUZXN0X2xnYm0pLA0KICAgICAgICAgICAgICAgICAgICBlYXJseV9zdG9wcGluZ19yb3VuZHMgPSA1MDApDQoNCiNiZXN0IGl0ZXI6IDY5DQojYmVzdCBzY29yZTogMTA5LjA2NTENCg0KIyBQcmVkaWN0aW9ucw0KcHJlZGljY2lvbmVzIDwtIHByZWRpY3QobW9kZWxvLCBkYXRhLm1hdHJpeChuZXdEYXRhVGVzdCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSkpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICBudW1faXRlcmF0aW9uID0gbW9kZWxvJGJlc3RfaXRlcikNCg0KcHJlZGljY2lvbmVzW3ByZWRpY2Npb25lcyA8IDBdIDwtIDANCngxMSgpO2hpc3QocHJlZGljY2lvbmVzKQ0KDQojIFN1Ym1pc3Npb24NCmRhdGFTYW1wbGUgJT4lIA0KICBzZWxlY3QoSUQpICU+JSANCiAgbXV0YXRlKHRhcmdldCA9IHByZWRpY2Npb25lcykgLT4NCiAgbGdibVIyDQoNCiMgRXhwb3J0IHN1Ym1pc3Npb24gZm9yIHppbmRpDQp3cml0ZS5jc3YobGdibVIyLCBmaWxlID0gIi4uL3N1Ym1pc3Npb24vbGdibVIyVnVlbG9zLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCg0KIyMgTW9kZWxvIDMNCg0KYGBge3J9DQojIExvYWQgZGF0YQ0KbG9hZCgiLi4vbXlEYXRhL1RyYWluMS5SZGF0YSIpDQpsb2FkKCIuLi9teURhdGEvVGVzdDEuUmRhdGEiKQ0KZGF0YVNhbXBsZSA8LSBkYXRhLnRhYmxlOjpmcmVhZCgiLi4vZGF0YS9zYW1wbGUuY3N2IikNCg0KbmV3RGF0YVRyYWluIDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRyYWluKQ0KbmV3RGF0YVRlc3QgPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVGVzdCkNCg0KIyBTZWxlY3Rpb24gb2YgdmFyaWFibGVzIGZvciBhbmFseXNpcyANCmxpYnJhcnkodGlkeXZlcnNlKQ0KZGF0YVRyYWluIDwtIG5ld0RhdGFUcmFpbiAlPiUNCiAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEpKQ0KDQojIGNhcmV0IGZvciBwYXJ0aXRpb24gZGF0YQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDEyMykNCmluZHggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5ID0gZGF0YVRyYWluJHRhcmdldCwgdGltZXMgPSAxLCBwID0gMC44MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UpDQpkZlRyYWluIDwtIGRhdGFUcmFpbltpbmR4LCBdDQpkZlRlc3QgPC0gZGF0YVRyYWluWy1pbmR4LCBdDQoNCiMgQ2F0ZWdvcmljYWwgZmVhdHVyZXMNCmNhdEZlYXR1cmVzIDwtIG5hbWVzKA0KICBkYXRhVHJhaW4gJT4lIHNlbGVjdF9pZihpcy5mYWN0b3IpDQopDQoNCiMgRGF0YSBmb3IgbGlnaHRnYm0NCmxpYnJhcnkobGlnaHRnYm0pDQpkYXRhVHJhaW5fbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGZUcmFpblssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRmVHJhaW5bLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCmRhdGFUZXN0X2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRmVGVzdFssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGZUZXN0WywgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCg0KIyBQYXJhbWV0ZXJzIGZvciBsaWdodGdibQ0KbXlQYXJhbXMgPC0gbGlzdCgNCiAgYm9vc3RpbmcgPSAiZ2JkdCIsDQogIG9iamVjdGl2ZSA9ICJyZWdyZXNzaW9uIiwNCiAgbWV0cmljID0gInJtc2UiLA0KICBsZWFybmluZ19yYXRlID0gMC4wMSwNCiAgZmVhdHVyZV9mcmFjdGlvbiA9IDEsDQogIGJhZ2dpbmdfZnJhY3Rpb24gPSAxLA0KICBtYXhfZGVwdGggPSAtMQ0KKQ0KDQojIFRyYWluIG1vZGVsDQptb2RlbG8gPC0gbGdiLnRyYWluKHBhcmFtcyA9IG15UGFyYW1zLA0KICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YVRyYWluX2xnYm0sDQogICAgICAgICAgICAgICAgICAgIG5yb3VuZHMgPSAxMDAwMCwNCiAgICAgICAgICAgICAgICAgICAgdmFsaWRzID0gbGlzdCh0ZXN0ID0gZGF0YVRlc3RfbGdibSksDQogICAgICAgICAgICAgICAgICAgIGVhcmx5X3N0b3BwaW5nX3JvdW5kcyA9IDUwMCkNCg0KI2Jlc3QgaXRlcjogMTQyNg0KI2Jlc3Qgc2NvcmU6IDEwNi44Mzg0DQoNCiMgUHJlZGljdGlvbnMNCnByZWRpY2Npb25lcyA8LSBwcmVkaWN0KG1vZGVsbywgZGF0YS5tYXRyaXgobmV3RGF0YVRlc3QgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEpKSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgbnVtX2l0ZXJhdGlvbiA9IG1vZGVsbyRiZXN0X2l0ZXIpDQoNCnByZWRpY2Npb25lc1twcmVkaWNjaW9uZXMgPCAwXSA8LSAwDQp4MTEoKTtoaXN0KHByZWRpY2Npb25lcykNCg0KIyBTdWJtaXNzaW9uDQpkYXRhU2FtcGxlICU+JSANCiAgc2VsZWN0KElEKSAlPiUgDQogIG11dGF0ZSh0YXJnZXQgPSBwcmVkaWNjaW9uZXMpIC0+DQogIGxnYm1SMw0KDQojIEV4cG9ydCBzdWJtaXNzaW9uIGZvciB6aW5kaQ0Kd3JpdGUuY3N2KGxnYm1SMywgZmlsZSA9ICIuLi9zdWJtaXNzaW9uL2xnYm1SM1Z1ZWxvcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBNb2RlbG8gNA0KDQpgYGB7cn0NCiMgTG9hZCBkYXRhDQpsb2FkKCIuLi9teURhdGEvVHJhaW4xLlJkYXRhIikNCmxvYWQoIi4uL215RGF0YS9UZXN0MS5SZGF0YSIpDQpkYXRhU2FtcGxlIDwtIGRhdGEudGFibGU6OmZyZWFkKCIuLi9kYXRhL3NhbXBsZS5jc3YiKQ0KDQpuZXdEYXRhVHJhaW4gPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVHJhaW4pDQpuZXdEYXRhVGVzdCA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUZXN0KQ0KDQojIFNlbGVjdGlvbiBvZiB2YXJpYWJsZXMgZm9yIGFuYWx5c2lzIA0KbGlicmFyeSh0aWR5dmVyc2UpDQpkYXRhVHJhaW4gPC0gbmV3RGF0YVRyYWluICU+JQ0KICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSkpDQoNCiMgY2FyZXQgZm9yIHBhcnRpdGlvbiBkYXRhDQpsaWJyYXJ5KGNhcmV0KQ0Kc2V0LnNlZWQoMTIzKQ0KaW5keCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHkgPSBkYXRhVHJhaW4kdGFyZ2V0LCB0aW1lcyA9IDEsIHAgPSAwLjgwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSkNCmRmVHJhaW4gPC0gZGF0YVRyYWluW2luZHgsIF0NCmRmVGVzdCA8LSBkYXRhVHJhaW5bLWluZHgsIF0NCg0KIyBDYXRlZ29yaWNhbCBmZWF0dXJlcw0KY2F0RmVhdHVyZXMgPC0gbmFtZXMoDQogIGRhdGFUcmFpbiAlPiUgc2VsZWN0X2lmKGlzLmZhY3RvcikNCikNCg0KIyBEYXRhIGZvciBsaWdodGdibQ0KbGlicmFyeShsaWdodGdibSkNCmRhdGFUcmFpbl9sZ2JtIDwtIGxnYi5EYXRhc2V0KGRhdGEgPSBkYXRhLm1hdHJpeChkZlRyYWluWywgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGZUcmFpblssIDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KZGF0YVRlc3RfbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGZUZXN0WywgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBkZlRlc3RbLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KDQojIFBhcmFtZXRlcnMgZm9yIGxpZ2h0Z2JtDQpteVBhcmFtcyA8LSBsaXN0KA0KICBib29zdGluZyA9ICJnYmR0IiwNCiAgb2JqZWN0aXZlID0gInJlZ3Jlc3Npb24iLA0KICBtZXRyaWMgPSAicm1zZSIsDQogIGxlYXJuaW5nX3JhdGUgPSAwLjAxLA0KICBmZWF0dXJlX2ZyYWN0aW9uID0gMC41LA0KICBiYWdnaW5nX2ZyYWN0aW9uID0gMSwNCiAgbWluX2RhdGFfaW5fbGVhZiA9IDEwMCwNCiAgbnVtX2xlYXZlcyA9IDI1NSwNCiAgbWF4X2RlcHRoID0gLTENCikNCg0KIyBUcmFpbiBtb2RlbA0KbW9kZWxvIDwtIGxnYi50cmFpbihwYXJhbXMgPSBteVBhcmFtcywNCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFUcmFpbl9sZ2JtLA0KICAgICAgICAgICAgICAgICAgICBucm91bmRzID0gMTAwMDAsDQogICAgICAgICAgICAgICAgICAgIHZhbGlkcyA9IGxpc3QodGVzdCA9IGRhdGFUZXN0X2xnYm0pLA0KICAgICAgICAgICAgICAgICAgICBlYXJseV9zdG9wcGluZ19yb3VuZHMgPSA1MDApDQoNCiNiZXN0IGl0ZXI6IDg3OA0KI2Jlc3Qgc2NvcmU6IDEwNi45MjQ3DQoNCiMgUHJlZGljdGlvbnMNCnByZWRpY2Npb25lcyA8LSBwcmVkaWN0KG1vZGVsbywgZGF0YS5tYXRyaXgobmV3RGF0YVRlc3QgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEpKSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgbnVtX2l0ZXJhdGlvbiA9IG1vZGVsbyRiZXN0X2l0ZXIpDQoNCnByZWRpY2Npb25lc1twcmVkaWNjaW9uZXMgPCAwXSA8LSAwDQp4MTEoKTtoaXN0KHByZWRpY2Npb25lcykNCg0KIyBTdWJtaXNzaW9uDQpkYXRhU2FtcGxlICU+JSANCiAgc2VsZWN0KElEKSAlPiUgDQogIG11dGF0ZSh0YXJnZXQgPSBwcmVkaWNjaW9uZXMpIC0+DQogIGxnYm1SNA0KDQojIEV4cG9ydCBzdWJtaXNzaW9uIGZvciB6aW5kaQ0Kd3JpdGUuY3N2KGxnYm1SNCwgZmlsZSA9ICIuLi9zdWJtaXNzaW9uL2xnYm1SNFZ1ZWxvcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBNb2RlbG8gNQ0KDQpgYGB7cn0NCiMgTG9hZCBkYXRhDQpsb2FkKCIuLi9teURhdGEvVHJhaW4xLlJkYXRhIikNCmxvYWQoIi4uL215RGF0YS9UZXN0MS5SZGF0YSIpDQpkYXRhU2FtcGxlIDwtIGRhdGEudGFibGU6OmZyZWFkKCIuLi9kYXRhL3NhbXBsZS5jc3YiKQ0KDQpuZXdEYXRhVHJhaW4gPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVHJhaW4pDQpuZXdEYXRhVGVzdCA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUZXN0KQ0KDQojIFNlbGVjdGlvbiBvZiB2YXJpYWJsZXMgZm9yIGFuYWx5c2lzIA0KbGlicmFyeSh0aWR5dmVyc2UpDQpkYXRhVHJhaW4gPC0gbmV3RGF0YVRyYWluICU+JQ0KICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSkpDQoNCiMgY2FyZXQgZm9yIHBhcnRpdGlvbiBkYXRhDQpsaWJyYXJ5KGNhcmV0KQ0Kc2V0LnNlZWQoMTIzKQ0KaW5keCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHkgPSBkYXRhVHJhaW4kdGFyZ2V0LCB0aW1lcyA9IDEsIHAgPSAwLjgwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSkNCmRmVHJhaW4gPC0gZGF0YVRyYWluW2luZHgsIF0NCmRmVGVzdCA8LSBkYXRhVHJhaW5bLWluZHgsIF0NCg0KIyBDYXRlZ29yaWNhbCBmZWF0dXJlcw0KY2F0RmVhdHVyZXMgPC0gbmFtZXMoDQogIGRhdGFUcmFpbiAlPiUgc2VsZWN0X2lmKGlzLmZhY3RvcikNCikNCg0KIyBEYXRhIGZvciBsaWdodGdibQ0KbGlicmFyeShsaWdodGdibSkNCmRhdGFUcmFpbl9sZ2JtIDwtIGxnYi5EYXRhc2V0KGRhdGEgPSBkYXRhLm1hdHJpeChkZlRyYWluWywgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGZUcmFpblssIDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KZGF0YVRlc3RfbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGZUZXN0WywgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBkZlRlc3RbLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KDQojIFBhcmFtZXRlcnMgZm9yIGxpZ2h0Z2JtDQpteVBhcmFtcyA8LSBsaXN0KA0KICBib29zdGluZyA9ICJnYmR0IiwNCiAgb2JqZWN0aXZlID0gInJlZ3Jlc3Npb24iLA0KICBtZXRyaWMgPSAicm1zZSIsDQogIGxlYXJuaW5nX3JhdGUgPSAwLjAxLA0KICBmZWF0dXJlX2ZyYWN0aW9uID0gMSwNCiAgYmFnZ2luZ19mcmFjdGlvbiA9IDEsDQogIG1pbl9kYXRhX2luX2xlYWYgPSAzMDAsDQogIG51bV9sZWF2ZXMgPSA1MDAsDQogIG1heF9iaW4gPSA1MDAsDQogIG1heF9kZXB0aCA9IC0xDQopDQoNCiMgVHJhaW4gbW9kZWwNCm1vZGVsbyA8LSBsZ2IudHJhaW4ocGFyYW1zID0gbXlQYXJhbXMsDQogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhVHJhaW5fbGdibSwNCiAgICAgICAgICAgICAgICAgICAgbnJvdW5kcyA9IDEwMDAwLA0KICAgICAgICAgICAgICAgICAgICB2YWxpZHMgPSBsaXN0KHRlc3QgPSBkYXRhVGVzdF9sZ2JtKSwNCiAgICAgICAgICAgICAgICAgICAgZWFybHlfc3RvcHBpbmdfcm91bmRzID0gNTAwKQ0KDQojYmVzdCBpdGVyOiA2ODkNCiNiZXN0IHNjb3JlOiAxMDguMTE3Ng0KDQojIFByZWRpY3Rpb25zDQpwcmVkaWNjaW9uZXMgPC0gcHJlZGljdChtb2RlbG8sIGRhdGEubWF0cml4KG5ld0RhdGFUZXN0ICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBKSkpLCANCiAgICAgICAgICAgICAgICAgICAgICAgIG51bV9pdGVyYXRpb24gPSBtb2RlbG8kYmVzdF9pdGVyKQ0KDQpwcmVkaWNjaW9uZXNbcHJlZGljY2lvbmVzIDwgMF0gPC0gMA0KeDExKCk7aGlzdChwcmVkaWNjaW9uZXMpDQoNCiMgU3VibWlzc2lvbg0KZGF0YVNhbXBsZSAlPiUgDQogIHNlbGVjdChJRCkgJT4lIA0KICBtdXRhdGUodGFyZ2V0ID0gcHJlZGljY2lvbmVzKSAtPg0KICBsZ2JtUjUNCg0KIyBFeHBvcnQgc3VibWlzc2lvbiBmb3IgemluZGkNCndyaXRlLmNzdihsZ2JtUjUsIGZpbGUgPSAiLi4vc3VibWlzc2lvbi9sZ2JtUjVWdWVsb3MuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCg0KIyMgTW9kZWxvIDYNCg0KYGBge3J9DQojIExvYWQgZGF0YQ0KbG9hZCgiLi4vbXlEYXRhL1RyYWluMi5SZGF0YSIpDQpsb2FkKCIuLi9teURhdGEvVGVzdDIuUmRhdGEiKQ0KZGF0YVNhbXBsZSA8LSBkYXRhLnRhYmxlOjpmcmVhZCgiLi4vZGF0YS9zYW1wbGUuY3N2IikNCg0KbmV3RGF0YVRyYWluIDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRyYWluKQ0KbmV3RGF0YVRlc3QgPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVGVzdCkNCg0KIyBTZWxlY3Rpb24gb2YgdmFyaWFibGVzIGZvciBhbmFseXNpcyANCmxpYnJhcnkodGlkeXZlcnNlKQ0KZGF0YVRyYWluIDwtIG5ld0RhdGFUcmFpbiAlPiUNCiAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEsIG1pblJldHJhc28sIG1heFJldHJhc28pKQ0KDQojIGNhcmV0IGZvciBwYXJ0aXRpb24gZGF0YQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDEyMykNCmluZHggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5ID0gZGF0YVRyYWluJHRhcmdldCwgdGltZXMgPSAxLCBwID0gMC44MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UpDQpkZlRyYWluIDwtIGRhdGFUcmFpbltpbmR4LCBdDQpkZlRlc3QgPC0gZGF0YVRyYWluWy1pbmR4LCBdDQoNCiMgQ2F0ZWdvcmljYWwgZmVhdHVyZXMNCmNhdEZlYXR1cmVzIDwtIG5hbWVzKA0KICBkYXRhVHJhaW4gJT4lIHNlbGVjdF9pZihpcy5mYWN0b3IpDQopDQoNCiMgRGF0YSBmb3IgbGlnaHRnYm0NCmxpYnJhcnkobGlnaHRnYm0pDQpkYXRhVHJhaW5fbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGZUcmFpblssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRmVHJhaW5bLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCmRhdGFUZXN0X2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRmVGVzdFssIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGZUZXN0WywgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCg0KIyBQYXJhbWV0ZXJzIGZvciBsaWdodGdibQ0KbXlQYXJhbXMgPC0gbGlzdCgNCiAgYm9vc3RpbmcgPSAiZ2JkdCIsDQogIG9iamVjdGl2ZSA9ICJyZWdyZXNzaW9uIiwNCiAgbWV0cmljID0gInJtc2UiLA0KICBsZWFybmluZ19yYXRlID0gMC4wMSwNCiAgZmVhdHVyZV9mcmFjdGlvbiA9IDEsDQogIGJhZ2dpbmdfZnJhY3Rpb24gPSAxLA0KICBtYXhfZGVwdGggPSAtMQ0KKQ0KDQojIFRyYWluIG1vZGVsDQptb2RlbG8gPC0gbGdiLnRyYWluKHBhcmFtcyA9IG15UGFyYW1zLA0KICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YVRyYWluX2xnYm0sDQogICAgICAgICAgICAgICAgICAgIG5yb3VuZHMgPSAxMDAwMCwNCiAgICAgICAgICAgICAgICAgICAgdmFsaWRzID0gbGlzdCh0ZXN0ID0gZGF0YVRlc3RfbGdibSksDQogICAgICAgICAgICAgICAgICAgIGVhcmx5X3N0b3BwaW5nX3JvdW5kcyA9IDUwMCkNCg0KI2Jlc3QgaXRlcjogMTYyMA0KI2Jlc3Qgc2NvcmU6IDEwNS42NDg2DQoNCiMgUHJlZGljdGlvbnMNCnByZWRpY2Npb25lcyA8LSBwcmVkaWN0KG1vZGVsbywgZGF0YS5tYXRyaXgobmV3RGF0YVRlc3QgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pblJldHJhc28sIG1heFJldHJhc28pKSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgbnVtX2l0ZXJhdGlvbiA9IG1vZGVsbyRiZXN0X2l0ZXIpDQoNCnByZWRpY2Npb25lc1twcmVkaWNjaW9uZXMgPCAwXSA8LSAwDQp4MTEoKTtoaXN0KHByZWRpY2Npb25lcykNCg0KIyBTdWJtaXNzaW9uDQpkYXRhU2FtcGxlICU+JSANCiAgc2VsZWN0KElEKSAlPiUgDQogIG11dGF0ZSh0YXJnZXQgPSBwcmVkaWNjaW9uZXMpIC0+DQogIGxnYm1SNg0KDQojIEV4cG9ydCBzdWJtaXNzaW9uIGZvciB6aW5kaQ0Kd3JpdGUuY3N2KGxnYm1SNiwgZmlsZSA9ICIuLi9zdWJtaXNzaW9uL2xnYm1SNlZ1ZWxvcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBNb2RlbG8gNw0KDQpgYGB7cn0NCiMgTG9hZCBkYXRhDQpsb2FkKCIuLi9teURhdGEvVHJhaW4yLlJkYXRhIikNCmxvYWQoIi4uL215RGF0YS9UZXN0Mi5SZGF0YSIpDQpkYXRhU2FtcGxlIDwtIGRhdGEudGFibGU6OmZyZWFkKCIuLi9kYXRhL3NhbXBsZS5jc3YiKQ0KDQpuZXdEYXRhVHJhaW4gPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVHJhaW4pDQpuZXdEYXRhVGVzdCA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUZXN0KQ0KDQojIFNlbGVjdGlvbiBvZiB2YXJpYWJsZXMgZm9yIGFuYWx5c2lzIA0KbGlicmFyeSh0aWR5dmVyc2UpDQpkYXRhVHJhaW4gPC0gbmV3RGF0YVRyYWluICU+JQ0KICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSwgZGlhLCBtaW5SZXRyYXNvLCBtYXhSZXRyYXNvLCBSaWNSZXRyYXNvLCBmZWNoYUVzcGVjaWFsKSkNCg0KIyBjYXJldCBmb3IgcGFydGl0aW9uIGRhdGENCmxpYnJhcnkoY2FyZXQpDQpzZXQuc2VlZCgxMjMpDQppbmR4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeSA9IGRhdGFUcmFpbiR0YXJnZXQsIHRpbWVzID0gMSwgcCA9IDAuODAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCA9IEZBTFNFKQ0KZGZUcmFpbiA8LSBkYXRhVHJhaW5baW5keCwgXQ0KZGZUZXN0IDwtIGRhdGFUcmFpblstaW5keCwgXQ0KDQojIENhdGVnb3JpY2FsIGZlYXR1cmVzDQpjYXRGZWF0dXJlcyA8LSBuYW1lcygNCiAgZGF0YVRyYWluICU+JSBzZWxlY3RfaWYoaXMuZmFjdG9yKQ0KKQ0KDQojIERhdGEgZm9yIGxpZ2h0Z2JtDQpsaWJyYXJ5KGxpZ2h0Z2JtKQ0KZGF0YVRyYWluX2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRmVHJhaW5bLCAtMV0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBkZlRyYWluWywgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9mZWF0dXJlID0gY2F0RmVhdHVyZXMpDQpkYXRhVGVzdF9sZ2JtIDwtIGxnYi5EYXRhc2V0KGRhdGEgPSBkYXRhLm1hdHJpeChkZlRlc3RbLCAtMV0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRmVGVzdFssIDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9mZWF0dXJlID0gY2F0RmVhdHVyZXMpDQoNCiMgUGFyYW1ldGVycyBmb3IgbGlnaHRnYm0NCm15UGFyYW1zIDwtIGxpc3QoDQogIGJvb3N0aW5nID0gImdiZHQiLA0KICBvYmplY3RpdmUgPSAicmVncmVzc2lvbiIsDQogIG1ldHJpYyA9ICJybXNlIiwNCiAgbGVhcm5pbmdfcmF0ZSA9IDAuMDEsDQogIGZlYXR1cmVfZnJhY3Rpb24gPSAxLA0KICBiYWdnaW5nX2ZyYWN0aW9uID0gMSwNCiAgbWF4X2RlcHRoID0gLTENCikNCg0KIyBUcmFpbiBtb2RlbA0KbW9kZWxvIDwtIGxnYi50cmFpbihwYXJhbXMgPSBteVBhcmFtcywNCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFUcmFpbl9sZ2JtLA0KICAgICAgICAgICAgICAgICAgICBucm91bmRzID0gMTAwMDAsDQogICAgICAgICAgICAgICAgICAgIHZhbGlkcyA9IGxpc3QodGVzdCA9IGRhdGFUZXN0X2xnYm0pLA0KICAgICAgICAgICAgICAgICAgICBlYXJseV9zdG9wcGluZ19yb3VuZHMgPSA1MDApDQoNCiNiZXN0IGl0ZXI6IDE1MTENCiNiZXN0IHNjb3JlOiAxMDYuNjU0OQ0KDQojIFByZWRpY3Rpb25zDQpwcmVkaWNjaW9uZXMgPC0gcHJlZGljdChtb2RlbG8sIGRhdGEubWF0cml4KG5ld0RhdGFUZXN0ICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWEsIG1pblJldHJhc28sIG1heFJldHJhc28sIFJpY1JldHJhc28sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlY2hhRXNwZWNpYWwpKSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgbnVtX2l0ZXJhdGlvbiA9IG1vZGVsbyRiZXN0X2l0ZXIpDQoNCnByZWRpY2Npb25lc1twcmVkaWNjaW9uZXMgPCAwXSA8LSAwDQp4MTEoKTtoaXN0KHByZWRpY2Npb25lcykNCg0KIyBTdWJtaXNzaW9uDQpkYXRhU2FtcGxlICU+JSANCiAgc2VsZWN0KElEKSAlPiUgDQogIG11dGF0ZSh0YXJnZXQgPSBwcmVkaWNjaW9uZXMpIC0+DQogIGxnYm1SNw0KDQojIEV4cG9ydCBzdWJtaXNzaW9uIGZvciB6aW5kaQ0Kd3JpdGUuY3N2KGxnYm1SNywgZmlsZSA9ICIuLi9zdWJtaXNzaW9uL2xnYm1SN1Z1ZWxvcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBNb2RlbG8gOA0KDQpgYGB7cn0NCiMgTG9hZCBkYXRhDQpsb2FkKCIuLi9teURhdGEvVHJhaW4yLlJkYXRhIikNCmxvYWQoIi4uL215RGF0YS9UZXN0Mi5SZGF0YSIpDQpkYXRhU2FtcGxlIDwtIGRhdGEudGFibGU6OmZyZWFkKCIuLi9kYXRhL3NhbXBsZS5jc3YiKQ0KDQpuZXdEYXRhVHJhaW4gPC0gYXMuZGF0YS5mcmFtZShuZXdEYXRhVHJhaW4pDQpuZXdEYXRhVGVzdCA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUZXN0KQ0KDQojIFNlbGVjdGlvbiBvZiB2YXJpYWJsZXMgZm9yIGFuYWx5c2lzIA0KbGlicmFyeSh0aWR5dmVyc2UpDQpkYXRhVHJhaW4gPC0gbmV3RGF0YVRyYWluICU+JQ0KICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSwgZmVjaGFFc3BlY2lhbCkpDQoNCiMgY2FyZXQgZm9yIHBhcnRpdGlvbiBkYXRhDQpsaWJyYXJ5KGNhcmV0KQ0Kc2V0LnNlZWQoMTIzKQ0KaW5keCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHkgPSBkYXRhVHJhaW4kdGFyZ2V0LCB0aW1lcyA9IDEsIHAgPSAwLjgwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSkNCmRmVHJhaW4gPC0gZGF0YVRyYWluW2luZHgsIF0NCmRmVGVzdCA8LSBkYXRhVHJhaW5bLWluZHgsIF0NCg0KIyBDYXRlZ29yaWNhbCBmZWF0dXJlcw0KY2F0RmVhdHVyZXMgPC0gbmFtZXMoDQogIGRhdGFUcmFpbiAlPiUgc2VsZWN0X2lmKGlzLmZhY3RvcikNCikNCg0KIyBEYXRhIGZvciBsaWdodGdibQ0KbGlicmFyeShsaWdodGdibSkNCmRhdGFUcmFpbl9sZ2JtIDwtIGxnYi5EYXRhc2V0KGRhdGEgPSBkYXRhLm1hdHJpeChkZlRyYWluWywgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGZUcmFpblssIDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KZGF0YVRlc3RfbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGZUZXN0WywgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBkZlRlc3RbLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KDQojIFBhcmFtZXRlcnMgZm9yIGxpZ2h0Z2JtDQpteVBhcmFtcyA8LSBsaXN0KA0KICBib29zdGluZyA9ICJnYmR0IiwNCiAgb2JqZWN0aXZlID0gInJlZ3Jlc3Npb24iLA0KICBtZXRyaWMgPSAicm1zZSIsDQogIGxlYXJuaW5nX3JhdGUgPSAwLjAxLA0KICBmZWF0dXJlX2ZyYWN0aW9uID0gMSwNCiAgYmFnZ2luZ19mcmFjdGlvbiA9IDEsDQogIG1heF9kZXB0aCA9IC0xDQopDQoNCiMgVHJhaW4gbW9kZWwNCm1vZGVsbyA8LSBsZ2IudHJhaW4ocGFyYW1zID0gbXlQYXJhbXMsDQogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhVHJhaW5fbGdibSwNCiAgICAgICAgICAgICAgICAgICAgbnJvdW5kcyA9IDEwMDAwLA0KICAgICAgICAgICAgICAgICAgICB2YWxpZHMgPSBsaXN0KHRlc3QgPSBkYXRhVGVzdF9sZ2JtKSwNCiAgICAgICAgICAgICAgICAgICAgZWFybHlfc3RvcHBpbmdfcm91bmRzID0gNTAwKQ0KDQojYmVzdCBpdGVyOiAxNTA0DQojYmVzdCBzY29yZTogMTA1Ljc4NjYNCg0KIyBQcmVkaWN0aW9ucw0KcHJlZGljY2lvbmVzIDwtIHByZWRpY3QobW9kZWxvLCBkYXRhLm1hdHJpeChuZXdEYXRhVGVzdCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSwgZmVjaGFFc3BlY2lhbCkpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICBudW1faXRlcmF0aW9uID0gbW9kZWxvJGJlc3RfaXRlcikNCg0KcHJlZGljY2lvbmVzW3ByZWRpY2Npb25lcyA8IDBdIDwtIDANCngxMSgpO2hpc3QocHJlZGljY2lvbmVzKQ0KDQojIFN1Ym1pc3Npb24NCmRhdGFTYW1wbGUgJT4lIA0KICBzZWxlY3QoSUQpICU+JSANCiAgbXV0YXRlKHRhcmdldCA9IHByZWRpY2Npb25lcykgLT4NCiAgbGdibVI4DQoNCiMgRXhwb3J0IHN1Ym1pc3Npb24gZm9yIHppbmRpDQp3cml0ZS5jc3YobGdibVI4LCBmaWxlID0gIi4uL3N1Ym1pc3Npb24vbGdibVI4VnVlbG9zLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCiMjIE1vZGVsbyA5DQoNCmBgYHtyfQ0KIyBMb2FkIGRhdGENCmxvYWQoIi4uL215RGF0YS9UcmFpbjIuUmRhdGEiKQ0KbG9hZCgiLi4vbXlEYXRhL1Rlc3QyLlJkYXRhIikNCmRhdGFTYW1wbGUgPC0gZGF0YS50YWJsZTo6ZnJlYWQoIi4uL2RhdGEvc2FtcGxlLmNzdiIpDQoNCm5ld0RhdGFUcmFpbiA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUcmFpbikNCm5ld0RhdGFUZXN0IDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRlc3QpDQoNCiMgU2VsZWN0aW9uIG9mIHZhcmlhYmxlcyBmb3IgYW5hbHlzaXMgDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmRhdGFUcmFpbiA8LSBuZXdEYXRhVHJhaW4gJT4lDQogIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBLCBmZWNoYUVzcGVjaWFsKSkNCg0KIyBjYXJldCBmb3IgcGFydGl0aW9uIGRhdGENCmxpYnJhcnkoY2FyZXQpDQpzZXQuc2VlZCgxMjMpDQppbmR4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeSA9IGRhdGFUcmFpbiR0YXJnZXQsIHRpbWVzID0gMSwgcCA9IDAuOTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCA9IEZBTFNFKQ0KZGZUcmFpbiA8LSBkYXRhVHJhaW5baW5keCwgXQ0KZGZUZXN0IDwtIGRhdGFUcmFpblstaW5keCwgXQ0KDQojIENhdGVnb3JpY2FsIGZlYXR1cmVzDQpjYXRGZWF0dXJlcyA8LSBuYW1lcygNCiAgZGF0YVRyYWluICU+JSBzZWxlY3RfaWYoaXMuZmFjdG9yKQ0KKQ0KDQojIERhdGEgZm9yIGxpZ2h0Z2JtDQpsaWJyYXJ5KGxpZ2h0Z2JtKQ0KZGF0YVRyYWluX2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRmVHJhaW5bLCAtMV0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBkZlRyYWluWywgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9mZWF0dXJlID0gY2F0RmVhdHVyZXMpDQpkYXRhVGVzdF9sZ2JtIDwtIGxnYi5EYXRhc2V0KGRhdGEgPSBkYXRhLm1hdHJpeChkZlRlc3RbLCAtMV0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRmVGVzdFssIDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9mZWF0dXJlID0gY2F0RmVhdHVyZXMpDQoNCiMgUGFyYW1ldGVycyBmb3IgbGlnaHRnYm0NCm15UGFyYW1zIDwtIGxpc3QoDQogIGJvb3N0aW5nID0gImdiZHQiLA0KICBvYmplY3RpdmUgPSAicmVncmVzc2lvbiIsDQogIG1ldHJpYyA9ICJybXNlIiwNCiAgbGVhcm5pbmdfcmF0ZSA9IDAuMDEsDQogIGZlYXR1cmVfZnJhY3Rpb24gPSAxLA0KICBiYWdnaW5nX2ZyYWN0aW9uID0gMSwNCiAgbWF4X2RlcHRoID0gLTENCikNCg0KIyBUcmFpbiBtb2RlbA0KbW9kZWxvIDwtIGxnYi50cmFpbihwYXJhbXMgPSBteVBhcmFtcywNCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFUcmFpbl9sZ2JtLA0KICAgICAgICAgICAgICAgICAgICBucm91bmRzID0gMzAwMDAsDQogICAgICAgICAgICAgICAgICAgIHZhbGlkcyA9IGxpc3QodGVzdCA9IGRhdGFUZXN0X2xnYm0pLA0KICAgICAgICAgICAgICAgICAgICBlYXJseV9zdG9wcGluZ19yb3VuZHMgPSA1MDApDQoNCiNiZXN0IGl0ZXI6IDE5NTE5DQojYmVzdCBzY29yZTogMTAyLjE5ODgNCg0KIyBQcmVkaWN0aW9ucw0KcHJlZGljY2lvbmVzIDwtIHByZWRpY3QobW9kZWxvLCBkYXRhLm1hdHJpeChuZXdEYXRhVGVzdCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSwgZmVjaGFFc3BlY2lhbCkpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICBudW1faXRlcmF0aW9uID0gbW9kZWxvJGJlc3RfaXRlcikNCg0KcHJlZGljY2lvbmVzW3ByZWRpY2Npb25lcyA8IDBdIDwtIDANCngxMSgpO2hpc3QocHJlZGljY2lvbmVzKQ0KDQojIFN1Ym1pc3Npb24NCmRhdGFTYW1wbGUgJT4lIA0KICBzZWxlY3QoSUQpICU+JSANCiAgbXV0YXRlKHRhcmdldCA9IHByZWRpY2Npb25lcykgLT4NCiAgbGdibVI5DQoNCiMgRXhwb3J0IHN1Ym1pc3Npb24gZm9yIHppbmRpDQp3cml0ZS5jc3YobGdibVI5LCBmaWxlID0gIi4uL3N1Ym1pc3Npb24vbGdibVI5VnVlbG9zLmNzdiIsDQogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpDQoNCmBgYA0KDQojIyBNb2RlbG8gMTANCg0KYGBge3J9DQojIE1vZDEgKyBLRm9sZA0KIyBMb2FkIGRhdGENCmxvYWQoIi4uL215RGF0YS9UcmFpbjEuUmRhdGEiKQ0KbG9hZCgiLi4vbXlEYXRhL1Rlc3QxLlJkYXRhIikNCmRhdGFTYW1wbGUgPC0gZGF0YS50YWJsZTo6ZnJlYWQoIi4uL2RhdGEvc2FtcGxlLmNzdiIpDQoNCm5ld0RhdGFUcmFpbiA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUcmFpbikNCm5ld0RhdGFUZXN0IDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRlc3QpDQoNCiMgU2VsZWN0aW9uIG9mIHZhcmlhYmxlcyBmb3IgYW5hbHlzaXMgDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmRhdGFUcmFpbiA8LSBuZXdEYXRhVHJhaW4gJT4lDQogIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBKSkNCg0KIyBDYXRlZ29yaWNhbCBmZWF0dXJlcw0KY2F0RmVhdHVyZXMgPC0gbmFtZXMoDQogIGRhdGFUcmFpbiAlPiUgc2VsZWN0X2lmKGlzLmZhY3RvcikNCikNCg0KIyBjYXJldCBmb3IgcGFydGl0aW9uIGRhdGEgd2l0aCByZXNhbXBsZQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDEyMykNCmluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeSA9IGRhdGFUcmFpbiR0YXJnZXQsIHRpbWVzID0gMTAsIHAgPSAwLjcwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gVFJVRSkNCg0KIyBQYXJhbWV0ZXJzIGZvciBsaWdodGdibQ0KbXlQYXJhbXMgPC0gbGlzdCgNCiAgYm9vc3RpbmcgPSAiZ2JkdCIsDQogIG9iamVjdGl2ZSA9ICJyZWdyZXNzaW9uIiwNCiAgbWV0cmljID0gInJtc2UiLA0KICBsZWFybmluZ19yYXRlID0gMC4xLA0KICBmZWF0dXJlX2ZyYWN0aW9uID0gMSwNCiAgYmFnZ2luZ19mcmFjdGlvbiA9IDEsDQogIG1heF9kZXB0aCA9IC0xDQopDQoNCiMgbGlnaHRnYm0gd2l0aCBLLUZvbGQgbWFudWFsbHkgKGsgPSAxMCkNCmxpYnJhcnkobGlnaHRnYm0pDQprIDwtIDEwDQpwcmVkVGVzdCA8LSBsaXN0KCkNCmJlc3RTY29yZSA8LSBjKCkNCmZvciAoaSBpbiAxOmspIHsNCiAgDQogICMgRGF0YSB0cmFpbiBhbmQgdGVzdA0KICBkYXRhVHJhaW5fbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGF0YVRyYWluW2luZGV4W1tpXV0sIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGF0YVRyYWluW2luZGV4W1tpXV0sIDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9mZWF0dXJlID0gY2F0RmVhdHVyZXMpDQogIGRhdGFUZXN0X2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRhdGFUcmFpblstaW5kZXhbW2ldXSwgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRhdGFUcmFpblstaW5kZXhbW2ldXSwgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KICANCiAgIyBUcmFpbiBtb2RlbA0KICBtb2RlbCA8LSBsZ2IudHJhaW4ocGFyYW1zID0gbXlQYXJhbXMsDQogICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YVRyYWluX2xnYm0sDQogICAgICAgICAgICAgICAgICAgICBucm91bmRzID0gMTAwMDAsDQogICAgICAgICAgICAgICAgICAgICB2YWxpZHMgPSBsaXN0KHRlc3QgPSBkYXRhVGVzdF9sZ2JtKSwNCiAgICAgICAgICAgICAgICAgICAgIGVhcmx5X3N0b3BwaW5nX3JvdW5kcyA9IDUwMCkNCiAgDQogICMgUHJlZGljdGlvbnMNCiAgcHJlZGljdGlvbnMgPC0gcHJlZGljdChtb2RlbCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhLm1hdHJpeChuZXdEYXRhVGVzdCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBKSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG51bV9pdGVyYXRpb24gPSBtb2RlbCRiZXN0X2l0ZXIpDQogIHByZWRUZXN0W1tpXV0gPSBwcmVkaWN0aW9ucw0KICANCiAgIyBCZXN0IHNjb3JlIGZvciBpdGVyYXRpb24NCiAgYmVzdFNjb3JlW2ldID0gbW9kZWwkYmVzdF9zY29yZQ0KICANCiAgIyBOZXh0IGl0ZXJhdGlvbg0KICBjYXQoIkl0ZXJhdGlvbjo9PT09PT09PT09IiwgaSwgIlJTTUU6PT09PT09PT09PSIsIG1vZGVsJGJlc3Rfc2NvcmUsICJSZWFkeSEiKQ0KfQ0KDQojIE1lYW4gcHJlZGljdGlvbnMNCmRhdGFQcmVkIDwtIGFzLmRhdGEuZnJhbWUocHJlZFRlc3QpDQpuYW1lcyhkYXRhUHJlZCkgPC0gcGFzdGUwKCJNb2QiLCAxOjEwKQ0KcHJlZGljY2lvbmVzIDwtIGFwcGx5KGRhdGFQcmVkLCAxLCBtZWFuKQ0KDQpwcmVkaWNjaW9uZXNbcHJlZGljY2lvbmVzIDwgMF0gPC0gMA0KeDExKCk7aGlzdChwcmVkaWNjaW9uZXMpDQoNCiMgU3VibWlzc2lvbg0KZGF0YVNhbXBsZSAlPiUgDQogIHNlbGVjdChJRCkgJT4lIA0KICBtdXRhdGUodGFyZ2V0ID0gcHJlZGljY2lvbmVzKSAtPg0KICBsZ2JtUjEwDQoNCiMgRXhwb3J0IHN1Ym1pc3Npb24gZm9yIHppbmRpDQp3cml0ZS5jc3YobGdibVIxMCwgZmlsZSA9ICIuLi9zdWJtaXNzaW9uL2xnYm1SMTBWdWVsb3MuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQoNCmBgYA0KDQojIyBNb2RlbG8gMTENCg0KYGBge3J9DQojIE1vZDIgKyBLRm9sZA0KIyBMb2FkIGRhdGENCmxvYWQoIi4uL215RGF0YS9UcmFpbjEuUmRhdGEiKQ0KbG9hZCgiLi4vbXlEYXRhL1Rlc3QxLlJkYXRhIikNCmRhdGFTYW1wbGUgPC0gZGF0YS50YWJsZTo6ZnJlYWQoIi4uL2RhdGEvc2FtcGxlLmNzdiIpDQoNCm5ld0RhdGFUcmFpbiA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUcmFpbikNCm5ld0RhdGFUZXN0IDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRlc3QpDQoNCiMgU2VsZWN0aW9uIG9mIHZhcmlhYmxlcyBmb3IgYW5hbHlzaXMgDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmRhdGFUcmFpbiA8LSBuZXdEYXRhVHJhaW4gJT4lDQogIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBKSkNCg0KIyBDYXRlZ29yaWNhbCBmZWF0dXJlcw0KY2F0RmVhdHVyZXMgPC0gbmFtZXMoDQogIGRhdGFUcmFpbiAlPiUgc2VsZWN0X2lmKGlzLmZhY3RvcikNCikNCg0KIyBjYXJldCBmb3IgcGFydGl0aW9uIGRhdGEgd2l0aCByZXNhbXBsZQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDEyMykNCmluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeSA9IGRhdGFUcmFpbiR0YXJnZXQsIHRpbWVzID0gMTAsIHAgPSAwLjcwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gVFJVRSkNCg0KIyBQYXJhbWV0ZXJzIGZvciBsaWdodGdibQ0KbXlQYXJhbXMgPC0gbGlzdCgNCiAgYm9vc3RpbmcgPSAiZ2JkdCIsDQogIG9iamVjdGl2ZSA9ICJyZWdyZXNzaW9uIiwNCiAgbWV0cmljID0gInJtc2UiLA0KICBsZWFybmluZ19yYXRlID0gMC4xLA0KICBmZWF0dXJlX2ZyYWN0aW9uID0gMC41LA0KICBiYWdnaW5nX2ZyYWN0aW9uID0gMSwNCiAgbWluX2RhdGFfaW5fbGVhZiA9IDEwMCwNCiAgbnVtX2xlYXZlcyA9IDI1NSwNCiAgbWF4X2RlcHRoID0gLTENCikNCg0KIyBsaWdodGdibSB3aXRoIEstRm9sZCBtYW51YWxseSAoayA9IDEwKQ0KbGlicmFyeShsaWdodGdibSkNCmsgPC0gMTANCnByZWRUZXN0IDwtIGxpc3QoKQ0KYmVzdFNjb3JlIDwtIGMoKQ0KZm9yIChpIGluIDE6aykgew0KICANCiAgIyBEYXRhIHRyYWluIGFuZCB0ZXN0DQogIGRhdGFUcmFpbl9sZ2JtIDwtIGxnYi5EYXRhc2V0KGRhdGEgPSBkYXRhLm1hdHJpeChkYXRhVHJhaW5baW5kZXhbW2ldXSwgLTFdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBkYXRhVHJhaW5baW5kZXhbW2ldXSwgMV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCiAgZGF0YVRlc3RfbGdibSA8LSBsZ2IuRGF0YXNldChkYXRhID0gZGF0YS5tYXRyaXgoZGF0YVRyYWluWy1pbmRleFtbaV1dLCAtMV0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZGF0YVRyYWluWy1pbmRleFtbaV1dLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9mZWF0dXJlID0gY2F0RmVhdHVyZXMpDQogIA0KICAjIFRyYWluIG1vZGVsDQogIG1vZGVsIDwtIGxnYi50cmFpbihwYXJhbXMgPSBteVBhcmFtcywNCiAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhVHJhaW5fbGdibSwNCiAgICAgICAgICAgICAgICAgICAgIG5yb3VuZHMgPSAxMDAwMCwNCiAgICAgICAgICAgICAgICAgICAgIHZhbGlkcyA9IGxpc3QodGVzdCA9IGRhdGFUZXN0X2xnYm0pLA0KICAgICAgICAgICAgICAgICAgICAgZWFybHlfc3RvcHBpbmdfcm91bmRzID0gNTAwKQ0KICANCiAgIyBQcmVkaWN0aW9ucw0KICBwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KG1vZGVsLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEubWF0cml4KG5ld0RhdGFUZXN0ICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KC1jKElELCBEQVRPUCwgU1RELCBTVEEpKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgbnVtX2l0ZXJhdGlvbiA9IG1vZGVsJGJlc3RfaXRlcikNCiAgcHJlZFRlc3RbW2ldXSA9IHByZWRpY3Rpb25zDQogIA0KICAjIEJlc3Qgc2NvcmUgZm9yIGl0ZXJhdGlvbg0KICBiZXN0U2NvcmVbaV0gPSBtb2RlbCRiZXN0X3Njb3JlDQogIA0KICAjIE5leHQgaXRlcmF0aW9uDQogIGNhdCgiSXRlcmF0aW9uOj09PT09PT09PT0iLCBpLCAiUlNNRTo9PT09PT09PT09IiwgbW9kZWwkYmVzdF9zY29yZSwgIlJlYWR5ISIpDQp9DQoNCiMgTWVhbiBwcmVkaWN0aW9ucw0KZGF0YVByZWQgPC0gYXMuZGF0YS5mcmFtZShwcmVkVGVzdCkNCm5hbWVzKGRhdGFQcmVkKSA8LSBwYXN0ZTAoIk1vZCIsIDE6MTApDQpwcmVkaWNjaW9uZXMgPC0gYXBwbHkoZGF0YVByZWQsIDEsIG1lYW4pDQoNCnByZWRpY2Npb25lc1twcmVkaWNjaW9uZXMgPCAwXSA8LSAwDQp4MTEoKTtoaXN0KHByZWRpY2Npb25lcykNCg0KIyBTdWJtaXNzaW9uDQpkYXRhU2FtcGxlICU+JSANCiAgc2VsZWN0KElEKSAlPiUgDQogIG11dGF0ZSh0YXJnZXQgPSBwcmVkaWNjaW9uZXMpIC0+DQogIGxnYm1SMTENCg0KIyBFeHBvcnQgc3VibWlzc2lvbiBmb3IgemluZGkNCndyaXRlLmNzdihsZ2JtUjExLCBmaWxlID0gIi4uL3N1Ym1pc3Npb24vbGdibVIxMVZ1ZWxvcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBNb2RlbG8gMTINCg0KYGBge3J9DQojIE1vZDEgKyBLRm9sZA0KIyBMb2FkIGRhdGENCmxvYWQoIi4uL215RGF0YS9UcmFpbjIuUmRhdGEiKQ0KbG9hZCgiLi4vbXlEYXRhL1Rlc3QyLlJkYXRhIikNCmRhdGFTYW1wbGUgPC0gZGF0YS50YWJsZTo6ZnJlYWQoIi4uL2RhdGEvc2FtcGxlLmNzdiIpDQoNCm5ld0RhdGFUcmFpbiA8LSBhcy5kYXRhLmZyYW1lKG5ld0RhdGFUcmFpbikNCm5ld0RhdGFUZXN0IDwtIGFzLmRhdGEuZnJhbWUobmV3RGF0YVRlc3QpDQoNCiMgU2VsZWN0aW9uIG9mIHZhcmlhYmxlcyBmb3IgYW5hbHlzaXMgDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmRhdGFUcmFpbiA8LSBuZXdEYXRhVHJhaW4gJT4lDQogIHNlbGVjdCgtYyhJRCwgREFUT1AsIFNURCwgU1RBKSkNCg0KIyBDYXRlZ29yaWNhbCBmZWF0dXJlcw0KY2F0RmVhdHVyZXMgPC0gbmFtZXMoDQogIGRhdGFUcmFpbiAlPiUgc2VsZWN0X2lmKGlzLmZhY3RvcikNCikNCg0KIyBjYXJldCBmb3IgcGFydGl0aW9uIGRhdGEgd2l0aCByZXNhbXBsZQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDEyMykNCmluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeSA9IGRhdGFUcmFpbiR0YXJnZXQsIHRpbWVzID0gMTAsIHAgPSAwLjcwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gVFJVRSkNCg0KIyBQYXJhbWV0ZXJzIGZvciBsaWdodGdibQ0KbXlQYXJhbXMgPC0gbGlzdCgNCiAgYm9vc3RpbmcgPSAiZ2JkdCIsDQogIG9iamVjdGl2ZSA9ICJyZWdyZXNzaW9uIiwNCiAgbWV0cmljID0gInJtc2UiLA0KICBsZWFybmluZ19yYXRlID0gMC4wMSwNCiAgZmVhdHVyZV9mcmFjdGlvbiA9IDEsDQogIGJhZ2dpbmdfZnJhY3Rpb24gPSAxLA0KICBtYXhfZGVwdGggPSAtMQ0KKQ0KDQojIGxpZ2h0Z2JtIHdpdGggSy1Gb2xkIG1hbnVhbGx5IChrID0gMTApDQpsaWJyYXJ5KGxpZ2h0Z2JtKQ0KayA8LSAxMA0KcHJlZFRlc3QgPC0gbGlzdCgpDQpiZXN0U2NvcmUgPC0gYygpDQpmb3IgKGkgaW4gMTprKSB7DQogIA0KICAjIERhdGEgdHJhaW4gYW5kIHRlc3QNCiAgZGF0YVRyYWluX2xnYm0gPC0gbGdiLkRhdGFzZXQoZGF0YSA9IGRhdGEubWF0cml4KGRhdGFUcmFpbltpbmRleFtbaV1dLCAtMV0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRhdGFUcmFpbltpbmRleFtbaV1dLCAxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZmVhdHVyZSA9IGNhdEZlYXR1cmVzKQ0KICBkYXRhVGVzdF9sZ2JtIDwtIGxnYi5EYXRhc2V0KGRhdGEgPSBkYXRhLm1hdHJpeChkYXRhVHJhaW5bLWluZGV4W1tpXV0sIC0xXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBkYXRhVHJhaW5bLWluZGV4W1tpXV0sIDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2ZlYXR1cmUgPSBjYXRGZWF0dXJlcykNCiAgDQogICMgVHJhaW4gbW9kZWwNCiAgbW9kZWwgPC0gbGdiLnRyYWluKHBhcmFtcyA9IG15UGFyYW1zLA0KICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFUcmFpbl9sZ2JtLA0KICAgICAgICAgICAgICAgICAgICAgbnJvdW5kcyA9IDEwMDAwLA0KICAgICAgICAgICAgICAgICAgICAgdmFsaWRzID0gbGlzdCh0ZXN0ID0gZGF0YVRlc3RfbGdibSksDQogICAgICAgICAgICAgICAgICAgICBlYXJseV9zdG9wcGluZ19yb3VuZHMgPSA1MDApDQogIA0KICAjIFByZWRpY3Rpb25zDQogIHByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgZGF0YS5tYXRyaXgobmV3RGF0YVRlc3QgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoLWMoSUQsIERBVE9QLCBTVEQsIFNUQSkpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBudW1faXRlcmF0aW9uID0gbW9kZWwkYmVzdF9pdGVyKQ0KICBwcmVkVGVzdFtbaV1dID0gcHJlZGljdGlvbnMNCiAgDQogICMgQmVzdCBzY29yZSBmb3IgaXRlcmF0aW9uDQogIGJlc3RTY29yZVtpXSA9IG1vZGVsJGJlc3Rfc2NvcmUNCiAgDQogICMgTmV4dCBpdGVyYXRpb24NCiAgY2F0KCJJdGVyYXRpb246PT09PT09PT09PSIsIGksICJSU01FOj09PT09PT09PT0iLCBtb2RlbCRiZXN0X3Njb3JlLCAiUmVhZHkhIikNCn0NCg0KIyBNZWFuIHByZWRpY3Rpb25zDQpkYXRhUHJlZCA8LSBhcy5kYXRhLmZyYW1lKHByZWRUZXN0KQ0KbmFtZXMoZGF0YVByZWQpIDwtIHBhc3RlMCgiTW9kIiwgMToxMCkNCnByZWRpY2Npb25lcyA8LSBhcHBseShkYXRhUHJlZCwgMSwgbWVhbikNCg0KcHJlZGljY2lvbmVzW3ByZWRpY2Npb25lcyA8IDBdIDwtIDANCngxMSgpO2hpc3QocHJlZGljY2lvbmVzKQ0KDQojIFN1Ym1pc3Npb24NCmRhdGFTYW1wbGUgJT4lIA0KICBzZWxlY3QoSUQpICU+JSANCiAgbXV0YXRlKHRhcmdldCA9IHByZWRpY2Npb25lcykgLT4NCiAgbGdibVIxMg0KDQojIEV4cG9ydCBzdWJtaXNzaW9uIGZvciB6aW5kaQ0Kd3JpdGUuY3N2KGxnYm1SMTIsIGZpbGUgPSAiLi4vc3VibWlzc2lvbi9sZ2JtUjEyVnVlbG9zLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCg==